mirror of
https://github.com/beetbox/beets.git
synced 2026-03-06 13:12:00 +01:00
merge flexattr branch
This is a big change. Some things will probably break.
This commit is contained in:
commit
247e3b9e01
31 changed files with 838 additions and 803 deletions
|
|
@ -12,7 +12,7 @@
|
|||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
|
||||
__version__ = '1.2.2'
|
||||
__version__ = '1.3.0'
|
||||
__author__ = 'Adrian Sampson <adrian@radbox.org>'
|
||||
|
||||
import beets.library
|
||||
|
|
|
|||
|
|
@ -810,7 +810,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
|
||||
|
|
@ -866,7 +866,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)
|
||||
|
|
|
|||
876
beets/library.py
876
beets/library.py
File diff suppressed because it is too large
Load diff
|
|
@ -905,7 +905,7 @@ def update_items(lib, query, album, move, pretend):
|
|||
continue
|
||||
|
||||
# Read new data.
|
||||
old_data = dict(item.record)
|
||||
old_data = dict(item)
|
||||
try:
|
||||
item.read()
|
||||
except Exception as exc:
|
||||
|
|
@ -920,12 +920,12 @@ def update_items(lib, query, album, move, pretend):
|
|||
old_data['albumartist'] == old_data['artist'] == \
|
||||
item.artist:
|
||||
item.albumartist = old_data['albumartist']
|
||||
item.dirty['albumartist'] = False
|
||||
item._dirty.remove('albumartist')
|
||||
|
||||
# Get and save metadata changes.
|
||||
changes = {}
|
||||
for key in library.ITEM_KEYS_META:
|
||||
if item.dirty[key]:
|
||||
if key in item._dirty:
|
||||
changes[key] = old_data[key], getattr(item, key)
|
||||
if changes:
|
||||
# Something changed.
|
||||
|
|
@ -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:
|
||||
|
|
@ -959,17 +959,18 @@ def update_items(lib, query, album, move, pretend):
|
|||
if album_id is None: # Singletons.
|
||||
continue
|
||||
album = lib.get_album(album_id)
|
||||
if not album: # Empty albums have already been removed.
|
||||
if not album: # Empty albums have already been removed.
|
||||
log.debug('emptied album %i' % album_id)
|
||||
continue
|
||||
al_items = list(album.items())
|
||||
first_item = album.items().get()
|
||||
|
||||
# Update album structure to reflect an item in it.
|
||||
for key in library.ALBUM_KEYS_ITEM:
|
||||
setattr(album, key, getattr(al_items[0], key))
|
||||
album[key] = first_item[key]
|
||||
album.store()
|
||||
|
||||
# Move album art (and any inconsistent items).
|
||||
if move and lib.directory in ancestry(al_items[0].path):
|
||||
if move and lib.directory in ancestry(first_item.path):
|
||||
log.debug('moving album %i' % album_id)
|
||||
album.move()
|
||||
|
||||
|
|
@ -1099,6 +1100,8 @@ def _convert_type(key, value, album=False):
|
|||
`album` indicates whether to use album or item field definitions.
|
||||
"""
|
||||
fields = library.ALBUM_FIELDS if album else library.ITEM_FIELDS
|
||||
if key not in fields:
|
||||
return value
|
||||
typ = [f[1] for f in fields if f[0] == key][0]
|
||||
|
||||
if typ is bool:
|
||||
|
|
@ -1120,15 +1123,9 @@ def _convert_type(key, value, album=False):
|
|||
def modify_items(lib, mods, query, write, move, album, confirm):
|
||||
"""Modifies matching items according to key=value assignments."""
|
||||
# Parse key=value specifications into a dictionary.
|
||||
if album:
|
||||
allowed_keys = library.ALBUM_KEYS
|
||||
else:
|
||||
allowed_keys = library.ITEM_KEYS_WRITABLE + ['added']
|
||||
fsets = {}
|
||||
for mod in mods:
|
||||
key, value = mod.split('=', 1)
|
||||
if key not in allowed_keys:
|
||||
raise ui.UserError('"%s" is not a valid field' % key)
|
||||
fsets[key] = _convert_type(key, value, album)
|
||||
|
||||
# Get the items to modify.
|
||||
|
|
@ -1143,8 +1140,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
|
|||
|
||||
# Show each change.
|
||||
for field, value in fsets.iteritems():
|
||||
curval = getattr(obj, field)
|
||||
_showdiff(field, curval, value)
|
||||
_showdiff(field, obj.get(field), value)
|
||||
|
||||
# Confirm.
|
||||
if confirm:
|
||||
|
|
@ -1156,7 +1152,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
|
|||
with lib.transaction():
|
||||
for obj in objs:
|
||||
for field, value in fsets.iteritems():
|
||||
setattr(obj, field, value)
|
||||
obj[field] = value
|
||||
|
||||
if move:
|
||||
cur_path = obj.item_dir() if album else obj.path
|
||||
|
|
@ -1167,9 +1163,7 @@ def modify_items(lib, mods, query, write, move, album, confirm):
|
|||
else:
|
||||
lib.move(obj)
|
||||
|
||||
# When modifying items, we have to store them to the database.
|
||||
if not album:
|
||||
lib.store(obj)
|
||||
obj.store()
|
||||
|
||||
# Apply tags if requested.
|
||||
if write:
|
||||
|
|
@ -1226,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',))
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import beets
|
|||
from beets.plugins import BeetsPlugin
|
||||
import beets.ui
|
||||
from beets import vfs
|
||||
from beets import config
|
||||
from beets.util import bluelet
|
||||
from beets.library import ITEM_KEYS_WRITABLE
|
||||
|
||||
|
|
@ -931,12 +930,12 @@ class Server(BaseServer):
|
|||
def cmd_stats(self, conn):
|
||||
"""Sends some statistics about the library."""
|
||||
with self.lib.transaction() as tx:
|
||||
songs, totaltime = beets.library.TrueQuery().count(tx)
|
||||
|
||||
statement = 'SELECT COUNT(DISTINCT artist), ' \
|
||||
'COUNT(DISTINCT album) FROM items'
|
||||
result = tx.query(statement)[0]
|
||||
artists, albums = result[0], result[1]
|
||||
'COUNT(DISTINCT album), ' \
|
||||
'COUNT(id), ' \
|
||||
'SUM(length) ' \
|
||||
'FROM items'
|
||||
artists, albums, songs, totaltime = tx.query(statement)[0]
|
||||
|
||||
yield (u'artists: ' + unicode(artists),
|
||||
u'albums: ' + unicode(albums),
|
||||
|
|
@ -1046,8 +1045,11 @@ class Server(BaseServer):
|
|||
tag/value query.
|
||||
"""
|
||||
_, key = self._tagtype_lookup(tag)
|
||||
query = beets.library.MatchQuery(key, value)
|
||||
songs, playtime = query.count(self.lib)
|
||||
songs = 0
|
||||
playtime = 0.0
|
||||
for item in self.lib.items(beets.library.MatchQuery(key, value)):
|
||||
songs += 1
|
||||
playtime += item.length
|
||||
yield u'songs: ' + unicode(songs)
|
||||
yield u'playtime: ' + unicode(int(playtime))
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
@ -168,7 +168,7 @@ def convert_func(lib, opts, args):
|
|||
if opts.album:
|
||||
items = (i for a in lib.albums(ui.decargs(args)) for i in a.items())
|
||||
else:
|
||||
items = lib.items(ui.decargs(args))
|
||||
items = iter(lib.items(ui.decargs(args)))
|
||||
convert = [convert_item(lib, dest, keep_new, path_formats) for i in range(threads)]
|
||||
pipe = util.pipeline.Pipeline([items, convert])
|
||||
pipe.run_parallel()
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ def batch_fetch_art(lib, albums, force, maxwidth=None):
|
|||
|
||||
if path:
|
||||
album.set_art(path, False)
|
||||
album.store()
|
||||
message = 'found album art'
|
||||
else:
|
||||
message = 'no art found'
|
||||
|
|
@ -274,6 +275,7 @@ class FetchArtPlugin(BeetsPlugin):
|
|||
src_removed = config['import']['delete'].get(bool) or \
|
||||
config['import']['move'].get(bool)
|
||||
album.set_art(path, not src_removed)
|
||||
album.store()
|
||||
if src_removed:
|
||||
task.prune(path)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@
|
|||
"""
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.library import RegisteredFieldQuery
|
||||
from beets.library import FieldQuery
|
||||
import beets
|
||||
import difflib
|
||||
|
||||
|
||||
class FuzzyQuery(RegisteredFieldQuery):
|
||||
class FuzzyQuery(FieldQuery):
|
||||
@classmethod
|
||||
def value_match(self, pattern, val):
|
||||
# smartcase
|
||||
|
|
|
|||
|
|
@ -70,12 +70,10 @@ def compile_inline(python_code, album):
|
|||
is_expr = True
|
||||
|
||||
def _dict_for(obj):
|
||||
out = dict(obj)
|
||||
if album:
|
||||
out = dict(obj._record)
|
||||
out['items'] = list(obj.items())
|
||||
return out
|
||||
else:
|
||||
return dict(obj.record)
|
||||
return out
|
||||
|
||||
if is_expr:
|
||||
# For expressions, just evaluate and return the result.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ from beets import config
|
|||
log = logging.getLogger('beets')
|
||||
|
||||
|
||||
def _print_and_apply_changes(lib, item, move, pretend, write):
|
||||
def _print_and_apply_changes(lib, item, old_data, move, pretend, write):
|
||||
"""Apply changes to an Item and preview them in the console. Return
|
||||
a boolean indicating whether any changes were made.
|
||||
"""
|
||||
changes = {}
|
||||
for key in library.ITEM_KEYS_META:
|
||||
if item.dirty[key]:
|
||||
changes[key] = item.old_data[key], getattr(item, key)
|
||||
if key in item._dirty:
|
||||
changes[key] = old_data[key], getattr(item, key)
|
||||
if not changes:
|
||||
return False
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ def mbsync_singletons(lib, query, move, pretend, write):
|
|||
.format(s.title))
|
||||
continue
|
||||
|
||||
s.old_data = dict(s.record)
|
||||
old_data = dict(s)
|
||||
|
||||
# Get the MusicBrainz recording info.
|
||||
track_info = hooks.track_for_mbid(s.mb_trackid)
|
||||
|
|
@ -80,7 +80,7 @@ def mbsync_singletons(lib, query, move, pretend, write):
|
|||
# Apply.
|
||||
with lib.transaction():
|
||||
autotag.apply_item_metadata(s, track_info)
|
||||
_print_and_apply_changes(lib, s, move, pretend, write)
|
||||
_print_and_apply_changes(lib, s, old_data, move, pretend, write)
|
||||
|
||||
|
||||
def mbsync_albums(lib, query, move, pretend, write):
|
||||
|
|
@ -93,8 +93,7 @@ def mbsync_albums(lib, query, move, pretend, write):
|
|||
continue
|
||||
|
||||
items = list(a.items())
|
||||
for item in items:
|
||||
item.old_data = dict(item.record)
|
||||
old_data = {item: dict(item) for item in items}
|
||||
|
||||
# Get the MusicBrainz album information.
|
||||
album_info = hooks.album_for_mbid(a.mb_albumid)
|
||||
|
|
@ -116,8 +115,8 @@ def mbsync_albums(lib, query, move, pretend, write):
|
|||
autotag.apply_metadata(album_info, mapping)
|
||||
changed = False
|
||||
for item in items:
|
||||
changed = _print_and_apply_changes(lib, item, move, pretend,
|
||||
write) or changed
|
||||
changed |= _print_and_apply_changes(lib, item, old_data[item],
|
||||
move, pretend, write)
|
||||
if not changed:
|
||||
# No change to any item.
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -61,39 +61,41 @@ def _item(track_info, album_info, album_id):
|
|||
t = track_info
|
||||
a = album_info
|
||||
|
||||
return Item({'album_id': album_id,
|
||||
'album': a.album,
|
||||
'albumartist': a.artist,
|
||||
'albumartist_credit': a.artist_credit,
|
||||
'albumartist_sort': a.artist_sort,
|
||||
'albumdisambig': a.albumdisambig,
|
||||
'albumstatus': a.albumstatus,
|
||||
'albumtype': a.albumtype,
|
||||
'artist': t.artist,
|
||||
'artist_credit': t.artist_credit,
|
||||
'artist_sort': t.artist_sort,
|
||||
'asin': a.asin,
|
||||
'catalognum': a.catalognum,
|
||||
'comp': a.va,
|
||||
'country': a.country,
|
||||
'day': a.day,
|
||||
'disc': t.medium,
|
||||
'disctitle': t.disctitle,
|
||||
'disctotal': a.mediums,
|
||||
'label': a.label,
|
||||
'language': a.language,
|
||||
'length': t.length,
|
||||
'mb_albumid': a.album_id,
|
||||
'mb_artistid': t.artist_id,
|
||||
'mb_releasegroupid': a.releasegroup_id,
|
||||
'mb_trackid': t.track_id,
|
||||
'media': a.media,
|
||||
'month': a.month,
|
||||
'script': a.script,
|
||||
'title': t.title,
|
||||
'track': t.index,
|
||||
'tracktotal': len(a.tracks),
|
||||
'year': a.year})
|
||||
return Item(
|
||||
album_id = album_id,
|
||||
album = a.album,
|
||||
albumartist = a.artist,
|
||||
albumartist_credit = a.artist_credit,
|
||||
albumartist_sort = a.artist_sort,
|
||||
albumdisambig = a.albumdisambig,
|
||||
albumstatus = a.albumstatus,
|
||||
albumtype = a.albumtype,
|
||||
artist = t.artist,
|
||||
artist_credit = t.artist_credit,
|
||||
artist_sort = t.artist_sort,
|
||||
asin = a.asin,
|
||||
catalognum = a.catalognum,
|
||||
comp = a.va,
|
||||
country = a.country,
|
||||
day = a.day,
|
||||
disc = t.medium,
|
||||
disctitle = t.disctitle,
|
||||
disctotal = a.mediums,
|
||||
label = a.label,
|
||||
language = a.language,
|
||||
length = t.length,
|
||||
mb_albumid = a.album_id,
|
||||
mb_artistid = t.artist_id,
|
||||
mb_releasegroupid = a.releasegroup_id,
|
||||
mb_trackid = t.track_id,
|
||||
media = a.media,
|
||||
month = a.month,
|
||||
script = a.script,
|
||||
title = t.title,
|
||||
track = t.index,
|
||||
tracktotal = len(a.tracks),
|
||||
year = a.year,
|
||||
)
|
||||
|
||||
|
||||
class MissingPlugin(BeetsPlugin):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def _rep(obj, expand=False):
|
|||
included.
|
||||
"""
|
||||
if isinstance(obj, beets.library.Item):
|
||||
out = dict(obj.record)
|
||||
out = dict(obj)
|
||||
del out['path']
|
||||
|
||||
# Get the size (in bytes) of the backing file. This is useful
|
||||
|
|
|
|||
|
|
@ -1,6 +1,39 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
1.3.0 (in development)
|
||||
----------------------
|
||||
|
||||
Albums and items now have **flexible attributes**. This means that, when you
|
||||
want to store information about your music in the beets database, you're no
|
||||
longer constrained to the set of fields it supports out of the box (title,
|
||||
artist, track, etc.). Instead, you can use any field name you can think of and
|
||||
treat it just like the built-in fields.
|
||||
|
||||
For example, you can use the :ref:`modify-cmd` command to set a new field on a
|
||||
track::
|
||||
|
||||
$ beet modify mood=sexy artist:miguel
|
||||
|
||||
and then query your music based on that field::
|
||||
|
||||
$ beet ls mood:sunny
|
||||
|
||||
or use templates to see the value of the field::
|
||||
|
||||
$ beet ls -f '$title: $mood'
|
||||
|
||||
While this feature is nifty when used directly with the usual command-line
|
||||
suspects, it's especially useful for plugin authors and for future beets
|
||||
features. Stay tuned for great things built on this flexible attribute
|
||||
infrastructure.
|
||||
|
||||
One side effect of this change: queries that include unknown fields will now
|
||||
match *nothing* instead of *everything*. So if you type ``beet ls
|
||||
fieldThatDoesNotExist:foo``, beets will now return no results, whereas
|
||||
previous versions would spit out a warning and then list your entire library.
|
||||
|
||||
|
||||
1.2.2 (August 27, 2013)
|
||||
-----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ master_doc = 'index'
|
|||
project = u'beets'
|
||||
copyright = u'2012, Adrian Sampson'
|
||||
|
||||
version = '1.2'
|
||||
release = '1.2.2'
|
||||
version = '1.3'
|
||||
release = '1.3.0'
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
|
|
|
|||
|
|
@ -357,18 +357,18 @@ To do so, define a subclass of the ``Query`` type from the ``beets.library``
|
|||
module. Then, in the ``queries`` method of your plugin class, return a
|
||||
dictionary mapping prefix strings to query classes.
|
||||
|
||||
One simple kind of query you can extend is the ``RegisteredFieldQuery``, which
|
||||
implements string comparisons. To use it, create a subclass inheriting from
|
||||
that class and override the ``value_match`` class method. (Remember the
|
||||
``@classmethod`` decorator!) The following example plugin declares a query
|
||||
using the ``@`` prefix to delimit exact string matches. The plugin will be
|
||||
used if we issue a command like ``beet ls @something`` or ``beet ls
|
||||
artist:@something``::
|
||||
One simple kind of query you can extend is the ``FieldQuery``, which
|
||||
implements string comparisons on fields. To use it, create a subclass
|
||||
inheriting from that class and override the ``value_match`` class method.
|
||||
(Remember the ``@classmethod`` decorator!) The following example plugin
|
||||
declares a query using the ``@`` prefix to delimit exact string matches. The
|
||||
plugin will be used if we issue a command like ``beet ls @something`` or
|
||||
``beet ls artist:@something``::
|
||||
|
||||
from beets.plugins import BeetsPlugin
|
||||
from beets.library import PluginQuery
|
||||
from beets.library import FieldQuery
|
||||
|
||||
class ExactMatchQuery(PluginQuery):
|
||||
class ExactMatchQuery(FieldQuery):
|
||||
@classmethod
|
||||
def value_match(self, pattern, val):
|
||||
return pattern == val
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -42,7 +42,7 @@ if 'sdist' in sys.argv:
|
|||
shutil.copytree(os.path.join(docdir, '_build', 'man'), mandir)
|
||||
|
||||
setup(name='beets',
|
||||
version='1.2.2',
|
||||
version='1.3.0',
|
||||
description='music tagger and library organizer',
|
||||
author='Adrian Sampson',
|
||||
author_email='adrian@radbox.org',
|
||||
|
|
|
|||
|
|
@ -45,35 +45,35 @@ _item_ident = 0
|
|||
def item():
|
||||
global _item_ident
|
||||
_item_ident += 1
|
||||
return beets.library.Item({
|
||||
'title': u'the title',
|
||||
'artist': u'the artist',
|
||||
'albumartist': u'the album artist',
|
||||
'album': u'the album',
|
||||
'genre': u'the genre',
|
||||
'composer': u'the composer',
|
||||
'grouping': u'the grouping',
|
||||
'year': 1,
|
||||
'month': 2,
|
||||
'day': 3,
|
||||
'track': 4,
|
||||
'tracktotal': 5,
|
||||
'disc': 6,
|
||||
'disctotal': 7,
|
||||
'lyrics': u'the lyrics',
|
||||
'comments': u'the comments',
|
||||
'bpm': 8,
|
||||
'comp': True,
|
||||
'path': 'somepath' + str(_item_ident),
|
||||
'length': 60.0,
|
||||
'bitrate': 128000,
|
||||
'format': 'FLAC',
|
||||
'mb_trackid': 'someID-1',
|
||||
'mb_albumid': 'someID-2',
|
||||
'mb_artistid': 'someID-3',
|
||||
'mb_albumartistid': 'someID-4',
|
||||
'album_id': None,
|
||||
})
|
||||
return beets.library.Item(
|
||||
title = u'the title',
|
||||
artist = u'the artist',
|
||||
albumartist = u'the album artist',
|
||||
album = u'the album',
|
||||
genre = u'the genre',
|
||||
composer = u'the composer',
|
||||
grouping = u'the grouping',
|
||||
year = 1,
|
||||
month = 2,
|
||||
day = 3,
|
||||
track = 4,
|
||||
tracktotal = 5,
|
||||
disc = 6,
|
||||
disctotal = 7,
|
||||
lyrics = u'the lyrics',
|
||||
comments = u'the comments',
|
||||
bpm = 8,
|
||||
comp = True,
|
||||
path = 'somepath' + str(_item_ident),
|
||||
length = 60.0,
|
||||
bitrate = 128000,
|
||||
format = 'FLAC',
|
||||
mb_trackid = 'someID-1',
|
||||
mb_albumid = 'someID-2',
|
||||
mb_artistid = 'someID-3',
|
||||
mb_albumartistid = 'someID-4',
|
||||
album_id = None,
|
||||
)
|
||||
|
||||
# Dummy import session.
|
||||
def import_session(lib=None, logfile=None, paths=[], query=[], cli=False):
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -53,30 +53,30 @@ class PluralityTest(_common.TestCase):
|
|||
plurality([])
|
||||
|
||||
def test_current_metadata_finds_pluralities(self):
|
||||
items = [Item({'artist': 'The Beetles', 'album': 'The White Album'}),
|
||||
Item({'artist': 'The Beatles', 'album': 'The White Album'}),
|
||||
Item({'artist': 'The Beatles', 'album': 'Teh White Album'})]
|
||||
items = [Item(artist='The Beetles', album='The White Album'),
|
||||
Item(artist='The Beatles', album='The White Album'),
|
||||
Item(artist='The Beatles', album='Teh White Album')]
|
||||
likelies, consensus = match.current_metadata(items)
|
||||
self.assertEqual(likelies['artist'], 'The Beatles')
|
||||
self.assertEqual(likelies['album'], 'The White Album')
|
||||
self.assertFalse(consensus['artist'])
|
||||
|
||||
def test_current_metadata_artist_consensus(self):
|
||||
items = [Item({'artist': 'The Beatles', 'album': 'The White Album'}),
|
||||
Item({'artist': 'The Beatles', 'album': 'The White Album'}),
|
||||
Item({'artist': 'The Beatles', 'album': 'Teh White Album'})]
|
||||
items = [Item(artist='The Beatles', album='The White Album'),
|
||||
Item(artist='The Beatles', album='The White Album'),
|
||||
Item(artist='The Beatles', album='Teh White Album')]
|
||||
likelies, consensus = match.current_metadata(items)
|
||||
self.assertEqual(likelies['artist'], 'The Beatles')
|
||||
self.assertEqual(likelies['album'], 'The White Album')
|
||||
self.assertTrue(consensus['artist'])
|
||||
|
||||
def test_albumartist_consensus(self):
|
||||
items = [Item({'artist': 'tartist1', 'album': 'album',
|
||||
'albumartist': 'aartist'}),
|
||||
Item({'artist': 'tartist2', 'album': 'album',
|
||||
'albumartist': 'aartist'}),
|
||||
Item({'artist': 'tartist3', 'album': 'album',
|
||||
'albumartist': 'aartist'})]
|
||||
items = [Item(artist='tartist1', album='album',
|
||||
albumartist='aartist'),
|
||||
Item(artist='tartist2', album='album',
|
||||
albumartist='aartist'),
|
||||
Item(artist='tartist3', album='album',
|
||||
albumartist='aartist')]
|
||||
likelies, consensus = match.current_metadata(items)
|
||||
self.assertEqual(likelies['artist'], 'aartist')
|
||||
self.assertFalse(consensus['artist'])
|
||||
|
|
@ -85,19 +85,17 @@ class PluralityTest(_common.TestCase):
|
|||
fields = ['artist', 'album', 'albumartist', 'year', 'disctotal',
|
||||
'mb_albumid', 'label', 'catalognum', 'country', 'media',
|
||||
'albumdisambig']
|
||||
items = [Item(dict((f, '%s_%s' % (f, i or 1)) for f in fields))
|
||||
items = [Item(**dict((f, '%s_%s' % (f, i or 1)) for f in fields))
|
||||
for i in range(5)]
|
||||
likelies, _ = match.current_metadata(items)
|
||||
for f in fields:
|
||||
self.assertEqual(likelies[f], '%s_1' % f)
|
||||
|
||||
def _make_item(title, track, artist=u'some artist'):
|
||||
return Item({
|
||||
'title': title, 'track': track,
|
||||
'artist': artist, 'album': u'some album',
|
||||
'length': 1,
|
||||
'mb_trackid': '', 'mb_albumid': '', 'mb_artistid': '',
|
||||
})
|
||||
return Item(title=title, track=track,
|
||||
artist=artist, album=u'some album',
|
||||
length=1,
|
||||
mb_trackid='', mb_albumid='', mb_artistid='')
|
||||
|
||||
def _make_trackinfo():
|
||||
return [
|
||||
|
|
@ -560,10 +558,10 @@ class MultiDiscAlbumsInDirTest(_common.TestCase):
|
|||
|
||||
class AssignmentTest(unittest.TestCase):
|
||||
def item(self, title, track):
|
||||
return Item({
|
||||
'title': title, 'track': track,
|
||||
'mb_trackid': '', 'mb_albumid': '', 'mb_artistid': '',
|
||||
})
|
||||
return Item(
|
||||
title=title, track=track,
|
||||
mb_trackid='', mb_albumid='', mb_artistid='',
|
||||
)
|
||||
|
||||
def test_reorder_when_track_numbers_incorrect(self):
|
||||
items = []
|
||||
|
|
@ -640,14 +638,14 @@ class AssignmentTest(unittest.TestCase):
|
|||
def test_order_works_when_track_names_are_entirely_wrong(self):
|
||||
# A real-world test case contributed by a user.
|
||||
def item(i, length):
|
||||
return Item({
|
||||
'artist': u'ben harper',
|
||||
'album': u'burn to shine',
|
||||
'title': u'ben harper - Burn to Shine ' + str(i),
|
||||
'track': i,
|
||||
'length': length,
|
||||
'mb_trackid': '', 'mb_albumid': '', 'mb_artistid': '',
|
||||
})
|
||||
return Item(
|
||||
artist=u'ben harper',
|
||||
album=u'burn to shine',
|
||||
title=u'ben harper - Burn to Shine ' + str(i),
|
||||
track=i,
|
||||
length=length,
|
||||
mb_trackid='', mb_albumid='', mb_artistid='',
|
||||
)
|
||||
items = []
|
||||
items.append(item(1, 241.37243007106997))
|
||||
items.append(item(2, 342.27781704375036))
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ def remove_lib():
|
|||
if os.path.exists(TEMP_LIB):
|
||||
os.unlink(TEMP_LIB)
|
||||
def boracay(l):
|
||||
return beets.library.Item(
|
||||
l._connection().execute('select * from items where id=3').fetchone()
|
||||
return beets.library.Item(l,
|
||||
**l._connection().execute('select * from items where id=3').fetchone()
|
||||
)
|
||||
np = util.normpath
|
||||
|
||||
|
|
@ -56,13 +56,13 @@ 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.assertTrue(not self.i.dirty['artist'])
|
||||
self.i.load()
|
||||
self.assertTrue('artist' not in self.i._dirty)
|
||||
|
||||
class StoreTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -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']
|
||||
|
|
@ -82,8 +82,8 @@ class StoreTest(unittest.TestCase):
|
|||
|
||||
def test_store_only_writes_dirty_fields(self):
|
||||
original_genre = self.i.genre
|
||||
self.i.record['genre'] = 'beatboxing' # change value w/o dirtying
|
||||
self.lib.store(self.i)
|
||||
self.i._values_fixed['genre'] = 'beatboxing' # change w/o dirtying
|
||||
self.i.store()
|
||||
new_genre = self.lib._connection().execute(
|
||||
'select genre from items where '
|
||||
'title="Boracay"').fetchone()['genre']
|
||||
|
|
@ -91,8 +91,8 @@ class StoreTest(unittest.TestCase):
|
|||
|
||||
def test_store_clears_dirty_flags(self):
|
||||
self.i.composer = 'tvp'
|
||||
self.lib.store(self.i)
|
||||
self.assertTrue(not self.i.dirty['composer'])
|
||||
self.i.store()
|
||||
self.assertTrue('composer' not in self.i._dirty)
|
||||
|
||||
class AddTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -139,11 +139,11 @@ class GetSetTest(unittest.TestCase):
|
|||
|
||||
def test_set_sets_dirty_flag(self):
|
||||
self.i.comp = not self.i.comp
|
||||
self.assertTrue(self.i.dirty['comp'])
|
||||
self.assertTrue('comp' in self.i._dirty)
|
||||
|
||||
def test_set_does_not_dirty_if_value_unchanged(self):
|
||||
self.i.title = self.i.title
|
||||
self.assertTrue(not self.i.dirty['title'])
|
||||
self.assertTrue('title' not in self.i._dirty)
|
||||
|
||||
def test_invalid_field_raises_attributeerror(self):
|
||||
self.assertRaises(AttributeError, getattr, self.i, 'xyzzy')
|
||||
|
|
@ -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')
|
||||
|
||||
|
|
@ -795,20 +798,23 @@ class AlbumInfoTest(unittest.TestCase):
|
|||
def test_albuminfo_changes_affect_items(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.album = 'myNewAlbum'
|
||||
i = self.lib.items().next()
|
||||
ai.store()
|
||||
i = self.lib.items()[0]
|
||||
self.assertEqual(i.album, 'myNewAlbum')
|
||||
|
||||
def test_albuminfo_change_albumartist_changes_items(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.albumartist = 'myNewArtist'
|
||||
i = self.lib.items().next()
|
||||
ai.store()
|
||||
i = self.lib.items()[0]
|
||||
self.assertEqual(i.albumartist, 'myNewArtist')
|
||||
self.assertNotEqual(i.artist, 'myNewArtist')
|
||||
|
||||
def test_albuminfo_change_artist_does_not_change_items(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
ai.artist = 'myNewArtist'
|
||||
i = self.lib.items().next()
|
||||
ai.store()
|
||||
i = self.lib.items()[0]
|
||||
self.assertNotEqual(i.artist, 'myNewArtist')
|
||||
|
||||
def test_albuminfo_remove_removes_items(self):
|
||||
|
|
@ -824,15 +830,6 @@ class AlbumInfoTest(unittest.TestCase):
|
|||
self.lib.remove(self.i)
|
||||
self.assertEqual(len(self.lib.albums()), 0)
|
||||
|
||||
class BaseAlbumTest(_common.TestCase):
|
||||
def test_field_access(self):
|
||||
album = beets.library.BaseAlbum(None, {'fld1':'foo'})
|
||||
self.assertEqual(album.fld1, 'foo')
|
||||
|
||||
def test_field_access_unset_values(self):
|
||||
album = beets.library.BaseAlbum(None, {})
|
||||
self.assertRaises(AttributeError, getattr, album, 'field')
|
||||
|
||||
class ArtDestinationTest(_common.TestCase):
|
||||
def setUp(self):
|
||||
super(ArtDestinationTest, self).setUp()
|
||||
|
|
@ -887,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)
|
||||
|
||||
|
|
@ -912,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)
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,8 @@ class AlbumFileTest(_common.TestCase):
|
|||
def test_albuminfo_move_changes_paths(self):
|
||||
self.ai.album = 'newAlbumName'
|
||||
self.ai.move()
|
||||
self.lib.load(self.i)
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
||||
self.assert_('newAlbumName' in self.i.path)
|
||||
|
||||
|
|
@ -178,7 +179,8 @@ class AlbumFileTest(_common.TestCase):
|
|||
oldpath = self.i.path
|
||||
self.ai.album = 'newAlbumName'
|
||||
self.ai.move()
|
||||
self.lib.load(self.i)
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
||||
self.assertFalse(os.path.exists(oldpath))
|
||||
self.assertTrue(os.path.exists(self.i.path))
|
||||
|
|
@ -187,14 +189,16 @@ class AlbumFileTest(_common.TestCase):
|
|||
oldpath = self.i.path
|
||||
self.ai.album = 'newAlbumName'
|
||||
self.ai.move(True)
|
||||
self.lib.load(self.i)
|
||||
self.ai.store()
|
||||
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)
|
||||
|
||||
class ArtFileTest(_common.TestCase):
|
||||
|
|
@ -216,6 +220,7 @@ class ArtFileTest(_common.TestCase):
|
|||
self.art = self.lib.get_album(self.i).art_destination('something.jpg')
|
||||
touch(self.art)
|
||||
self.ai.artpath = self.art
|
||||
self.ai.store()
|
||||
# Alternate destination dir.
|
||||
self.otherdir = os.path.join(self.temp_dir, 'testotherdir')
|
||||
|
||||
|
|
@ -229,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))
|
||||
|
|
@ -239,7 +244,8 @@ class ArtFileTest(_common.TestCase):
|
|||
def test_art_moves_with_album_to_custom_dir(self):
|
||||
# Move the album to another directory.
|
||||
self.ai.move(basedir=self.otherdir)
|
||||
self.lib.load(self.i)
|
||||
self.ai.store()
|
||||
self.i.load()
|
||||
|
||||
# Art should be in new directory.
|
||||
self.assertNotExists(self.art)
|
||||
|
|
@ -344,7 +350,8 @@ class ArtFileTest(_common.TestCase):
|
|||
self.assertExists(oldartpath)
|
||||
|
||||
self.ai.album = 'different_album'
|
||||
self.lib.move(self.i)
|
||||
self.ai.store()
|
||||
self.lib.move(self.ai.items()[0])
|
||||
|
||||
artpath = self.lib.albums()[0].artpath
|
||||
self.assertTrue('different_album' in artpath)
|
||||
|
|
@ -421,6 +428,7 @@ class RemoveTest(_common.TestCase):
|
|||
artfile = os.path.join(self.temp_dir, 'testart.jpg')
|
||||
touch(artfile)
|
||||
self.ai.set_art(artfile)
|
||||
self.ai.store()
|
||||
|
||||
parent = os.path.dirname(self.i.path)
|
||||
self.lib.remove(self.i, True)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -406,7 +407,7 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
self._apply_asis([self.i])
|
||||
|
||||
# Get the item's path and import it again.
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
self._apply_asis([new_item])
|
||||
|
||||
|
|
@ -416,7 +417,7 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
def test_apply_existing_album_does_not_duplicate_album(self):
|
||||
# As above.
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
self._apply_asis([new_item])
|
||||
|
||||
|
|
@ -425,7 +426,7 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
|
||||
def test_apply_existing_singleton_does_not_duplicate_album(self):
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
self._apply_asis([new_item], False)
|
||||
|
||||
|
|
@ -440,7 +441,7 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
self._apply_asis([self.i])
|
||||
|
||||
# Import again with new metadata.
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
new_item.title = 'differentTitle'
|
||||
self._apply_asis([new_item])
|
||||
|
|
@ -454,12 +455,12 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
config['import']['copy'] = True
|
||||
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
new_item.title = 'differentTitle'
|
||||
self._apply_asis([new_item])
|
||||
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertTrue('differentTitle' in item.path)
|
||||
self.assertExists(item.path)
|
||||
|
||||
|
|
@ -468,12 +469,12 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
config['import']['copy'] = False
|
||||
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
new_item.title = 'differentTitle'
|
||||
self._apply_asis([new_item])
|
||||
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertFalse('differentTitle' in item.path)
|
||||
self.assertExists(item.path)
|
||||
|
||||
|
|
@ -481,13 +482,13 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
config['import']['copy'] = True
|
||||
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
oldpath = item.path
|
||||
new_item = library.Item.from_path(item.path)
|
||||
new_item.title = 'differentTitle'
|
||||
self._apply_asis([new_item])
|
||||
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertNotExists(oldpath)
|
||||
|
||||
def test_apply_existing_item_new_metadata_delete_enabled(self):
|
||||
|
|
@ -497,13 +498,13 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
config['import']['delete'] = True # !
|
||||
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
oldpath = item.path
|
||||
new_item = library.Item.from_path(item.path)
|
||||
new_item.title = 'differentTitle'
|
||||
self._apply_asis([new_item])
|
||||
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertNotExists(oldpath)
|
||||
self.assertTrue('differentTitle' in item.path)
|
||||
self.assertExists(item.path)
|
||||
|
|
@ -513,13 +514,13 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
config['import']['copy'] = True
|
||||
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
oldpath = item.path
|
||||
new_item = library.Item.from_path(item.path)
|
||||
self._apply_asis([new_item])
|
||||
|
||||
self.assertEqual(len(list(self.lib.items())), 1)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertEqual(oldpath, item.path)
|
||||
self.assertExists(oldpath)
|
||||
|
||||
|
|
@ -528,19 +529,19 @@ class ApplyExistingItemsTest(_common.TestCase):
|
|||
config['import']['delete'] = True # !
|
||||
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
new_item = library.Item.from_path(item.path)
|
||||
self._apply_asis([new_item])
|
||||
|
||||
self.assertEqual(len(list(self.lib.items())), 1)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertExists(item.path)
|
||||
|
||||
def test_same_album_does_not_duplicate(self):
|
||||
# With the -L flag, exactly the same item (with the same ID)
|
||||
# is re-imported. This test simulates that situation.
|
||||
self._apply_asis([self.i])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self._apply_asis([item])
|
||||
|
||||
# Should not be duplicated.
|
||||
|
|
@ -704,6 +705,7 @@ class DuplicateCheckTest(_common.TestCase):
|
|||
|
||||
def test_duplicate_va_album(self):
|
||||
self.album.albumartist = 'an album artist'
|
||||
self.album.store()
|
||||
res = importer._duplicate_check(self.lib,
|
||||
self._album_task(False, 'an album artist'))
|
||||
self.assertTrue(res)
|
||||
|
|
|
|||
|
|
@ -78,30 +78,30 @@ class AnyFieldQueryTest(unittest.TestCase):
|
|||
def test_no_restriction(self):
|
||||
q = beets.library.AnyFieldQuery('title', beets.library.ITEM_KEYS,
|
||||
beets.library.SubstringQuery)
|
||||
self.assertEqual(self.lib.items(q).next().title, 'the title')
|
||||
self.assertEqual(self.lib.items(q).get().title, 'the title')
|
||||
|
||||
def test_restriction_completeness(self):
|
||||
q = beets.library.AnyFieldQuery('title', ['title'],
|
||||
beets.library.SubstringQuery)
|
||||
self.assertEqual(self.lib.items(q).next().title, 'the title')
|
||||
self.assertEqual(self.lib.items(q).get().title, 'the title')
|
||||
|
||||
def test_restriction_soundness(self):
|
||||
q = beets.library.AnyFieldQuery('title', ['artist'],
|
||||
beets.library.SubstringQuery)
|
||||
self.assertRaises(StopIteration, self.lib.items(q).next)
|
||||
self.assertEqual(self.lib.items(q).get(), None)
|
||||
|
||||
# Convenient asserts for matching items.
|
||||
class AssertsMixin(object):
|
||||
def assert_matched(self, result_iterator, title):
|
||||
self.assertEqual(result_iterator.next().title, title)
|
||||
def assert_done(self, result_iterator):
|
||||
self.assertRaises(StopIteration, result_iterator.next)
|
||||
def assert_matched_all(self, result_iterator):
|
||||
self.assert_matched(result_iterator, 'Littlest Things')
|
||||
self.assert_matched(result_iterator, 'Take Pills')
|
||||
self.assert_matched(result_iterator, 'Lovers Who Uncover')
|
||||
self.assert_matched(result_iterator, 'Boracay')
|
||||
self.assert_done(result_iterator)
|
||||
def assert_matched(self, results, titles):
|
||||
self.assertEqual([i.title for i in results], titles)
|
||||
|
||||
def assert_matched_all(self, results):
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
class GetTest(unittest.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
|
|
@ -122,127 +122,126 @@ class GetTest(unittest.TestCase, AssertsMixin):
|
|||
def test_get_one_keyed_term(self):
|
||||
q = 'artist:Lil'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
|
||||
def test_get_one_keyed_regexp(self):
|
||||
q = r'artist::L.+y'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
|
||||
def test_get_one_unkeyed_term(self):
|
||||
q = 'Terry'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Boracay')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Boracay'])
|
||||
|
||||
def test_get_one_unkeyed_regexp(self):
|
||||
q = r':y$'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Boracay')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Boracay'])
|
||||
|
||||
def test_get_no_matches(self):
|
||||
q = 'popebear'
|
||||
results = self.lib.items(q)
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_invalid_key(self):
|
||||
q = 'pope:bear'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched_all(results)
|
||||
# Matches nothing since the flexattr is not present on the
|
||||
# objects.
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_term_case_insensitive(self):
|
||||
q = 'UNCoVER'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Lovers Who Uncover'])
|
||||
|
||||
def test_regexp_case_sensitive(self):
|
||||
q = r':UNCoVER'
|
||||
results = self.lib.items(q)
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [])
|
||||
q = r':Uncover'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Lovers Who Uncover'])
|
||||
|
||||
def test_term_case_insensitive_with_key(self):
|
||||
q = 'album:stiLL'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
|
||||
def test_key_case_insensitive(self):
|
||||
q = 'ArTiST:Allen'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
|
||||
def test_unkeyed_term_matches_multiple_columns(self):
|
||||
q = 'little'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_matched(results, 'Boracay')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
def test_unkeyed_regexp_matches_multiple_columns(self):
|
||||
q = r':^T'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Take Pills')
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_matched(results, 'Boracay')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
def test_keyed_term_matches_only_one_column(self):
|
||||
q = 'artist:little'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_matched(results, 'Boracay')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
def test_keyed_regexp_matches_only_one_column(self):
|
||||
q = r'album::\sS'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Lovers Who Uncover',
|
||||
])
|
||||
|
||||
def test_multiple_terms_narrow_search(self):
|
||||
q = 'little ones'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_matched(results, 'Boracay')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
def test_multiple_regexps_narrow_search(self):
|
||||
q = r':\sS :^T'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Lovers Who Uncover'])
|
||||
|
||||
def test_mixed_terms_regexps_narrow_search(self):
|
||||
q = r':\sS lily'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['Littlest Things'])
|
||||
|
||||
def test_single_year(self):
|
||||
q = 'year:2006'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Lovers Who Uncover',
|
||||
])
|
||||
|
||||
def test_year_range(self):
|
||||
q = 'year:2000..2010'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'Littlest Things')
|
||||
self.assert_matched(results, 'Take Pills')
|
||||
self.assert_matched(results, 'Lovers Who Uncover')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [
|
||||
'Littlest Things',
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
])
|
||||
|
||||
def test_bad_year(self):
|
||||
q = 'year:delete from items'
|
||||
|
|
@ -263,55 +262,48 @@ class MemoryGetTest(unittest.TestCase, AssertsMixin):
|
|||
def test_singleton_true(self):
|
||||
q = 'singleton:true'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'singleton item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['singleton item'])
|
||||
|
||||
def test_singleton_false(self):
|
||||
q = 'singleton:false'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'album item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['album item'])
|
||||
|
||||
def test_compilation_true(self):
|
||||
q = 'comp:true'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'album item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['album item'])
|
||||
|
||||
def test_compilation_false(self):
|
||||
q = 'comp:false'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'singleton item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['singleton item'])
|
||||
|
||||
def test_unknown_field_name_ignored(self):
|
||||
def test_unknown_field_name_no_results(self):
|
||||
q = 'xyzzy:nonsense'
|
||||
results = self.lib.items(q)
|
||||
titles = [i.title for i in results]
|
||||
self.assertTrue('singleton item' in titles)
|
||||
self.assertTrue('album item' in titles)
|
||||
self.assertEqual(len(titles), 2)
|
||||
self.assertEqual(titles, [])
|
||||
|
||||
def test_unknown_field_name_ignored_in_album_query(self):
|
||||
def test_unknown_field_name_no_results_in_album_query(self):
|
||||
q = 'xyzzy:nonsense'
|
||||
results = self.lib.albums(q)
|
||||
names = [a.album for a in results]
|
||||
self.assertEqual(names, ['the album'])
|
||||
self.assertEqual(names, [])
|
||||
|
||||
def test_item_field_name_ignored_in_album_query(self):
|
||||
def test_item_field_name_matches_nothing_in_album_query(self):
|
||||
q = 'format:nonsense'
|
||||
results = self.lib.albums(q)
|
||||
names = [a.album for a in results]
|
||||
self.assertEqual(names, ['the album'])
|
||||
self.assertEqual(names, [])
|
||||
|
||||
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)
|
||||
self.assert_matched(results, u'caf\xe9')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [u'caf\xe9'])
|
||||
|
||||
class MatchTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -369,58 +361,52 @@ class PathQueryTest(unittest.TestCase, AssertsMixin):
|
|||
def test_path_exact_match(self):
|
||||
q = 'path:/a/b/c.mp3'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'path item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
def test_parent_directory_no_slash(self):
|
||||
q = 'path:/a'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'path item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
def test_parent_directory_with_slash(self):
|
||||
q = 'path:/a/'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'path item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
def test_no_match(self):
|
||||
q = 'path:/xyzzy/'
|
||||
results = self.lib.items(q)
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_fragment_no_match(self):
|
||||
q = 'path:/b/'
|
||||
results = self.lib.items(q)
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_nonnorm_path(self):
|
||||
q = 'path:/x/../a/b'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'path item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
def test_slashed_query_matches_path(self):
|
||||
q = '/a/b'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'path item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
def test_non_slashed_does_not_match_path(self):
|
||||
q = 'c.mp3'
|
||||
results = self.lib.items(q)
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_slashes_in_explicit_field_does_not_match_path(self):
|
||||
q = 'title:/a/b'
|
||||
results = self.lib.items(q)
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, [])
|
||||
|
||||
def test_path_regex(self):
|
||||
q = 'path::\\.mp3$'
|
||||
results = self.lib.items(q)
|
||||
self.assert_matched(results, 'path item')
|
||||
self.assert_done(results)
|
||||
self.assert_matched(results, ['path item'])
|
||||
|
||||
class BrowseTest(unittest.TestCase, AssertsMixin):
|
||||
def setUp(self):
|
||||
|
|
@ -438,11 +424,12 @@ class BrowseTest(unittest.TestCase, AssertsMixin):
|
|||
|
||||
def test_item_list(self):
|
||||
items = self.lib.items()
|
||||
self.assert_matched(items, 'Littlest Things')
|
||||
self.assert_matched(items, 'Take Pills')
|
||||
self.assert_matched(items, 'Lovers Who Uncover')
|
||||
self.assert_matched(items, 'Boracay')
|
||||
self.assert_done(items)
|
||||
self.assert_matched(items, [
|
||||
'Littlest Things',
|
||||
'Take Pills',
|
||||
'Lovers Who Uncover',
|
||||
'Boracay',
|
||||
])
|
||||
|
||||
def test_albums_matches_album(self):
|
||||
albums = list(self.lib.albums('person'))
|
||||
|
|
@ -454,31 +441,11 @@ class BrowseTest(unittest.TestCase, AssertsMixin):
|
|||
|
||||
def test_items_matches_title(self):
|
||||
items = self.lib.items('boracay')
|
||||
self.assert_matched(items, 'Boracay')
|
||||
self.assert_done(items)
|
||||
self.assert_matched(items, ['Boracay'])
|
||||
|
||||
def test_items_does_not_match_year(self):
|
||||
items = self.lib.items('2007')
|
||||
self.assert_done(items)
|
||||
|
||||
class CountTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.item = some_item
|
||||
self.lib.add(self.item)
|
||||
|
||||
def test_count_gets_single_item(self):
|
||||
with self.lib.transaction() as tx:
|
||||
songs, totaltime = beets.library.TrueQuery().count(tx)
|
||||
self.assertEqual(songs, 1)
|
||||
self.assertEqual(totaltime, self.item.length)
|
||||
|
||||
def test_count_works_for_empty_library(self):
|
||||
self.lib.remove(self.item)
|
||||
with self.lib.transaction() as tx:
|
||||
songs, totaltime = beets.library.TrueQuery().count(tx)
|
||||
self.assertEqual(songs, 0)
|
||||
self.assertEqual(totaltime, 0.0)
|
||||
self.assert_matched(items, [])
|
||||
|
||||
class StringParseTest(unittest.TestCase):
|
||||
def test_single_field_query(self):
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import os
|
||||
import shutil
|
||||
import textwrap
|
||||
import logging
|
||||
import re
|
||||
import yaml
|
||||
|
||||
|
|
@ -55,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'])
|
||||
|
|
@ -162,7 +161,7 @@ class ModifyTest(_common.TestCase):
|
|||
|
||||
def test_modify_item_dbdata(self):
|
||||
self._modify(["title=newTitle"])
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertEqual(item.title, 'newTitle')
|
||||
|
||||
def test_modify_album_dbdata(self):
|
||||
|
|
@ -172,47 +171,47 @@ class ModifyTest(_common.TestCase):
|
|||
|
||||
def test_modify_item_tag_unmodified(self):
|
||||
self._modify(["title=newTitle"], write=False)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
item.read()
|
||||
self.assertEqual(item.title, 'full')
|
||||
|
||||
def test_modify_album_tag_unmodified(self):
|
||||
self._modify(["album=newAlbum"], write=False, album=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
item.read()
|
||||
self.assertEqual(item.album, 'the album')
|
||||
|
||||
def test_modify_item_tag(self):
|
||||
self._modify(["title=newTitle"], write=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
item.read()
|
||||
self.assertEqual(item.title, 'newTitle')
|
||||
|
||||
def test_modify_album_tag(self):
|
||||
self._modify(["album=newAlbum"], write=True, album=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
item.read()
|
||||
self.assertEqual(item.album, 'newAlbum')
|
||||
|
||||
def test_item_move(self):
|
||||
self._modify(["title=newTitle"], move=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertTrue('newTitle' in item.path)
|
||||
|
||||
def test_album_move(self):
|
||||
self._modify(["album=newAlbum"], move=True, album=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
item.read()
|
||||
self.assertTrue('newAlbum' in item.path)
|
||||
|
||||
def test_item_not_move(self):
|
||||
self._modify(["title=newTitle"], move=False)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertFalse('newTitle' in item.path)
|
||||
|
||||
def test_album_not_move(self):
|
||||
self._modify(["album=newAlbum"], move=False, album=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
item.read()
|
||||
self.assertFalse('newAlbum' in item.path)
|
||||
|
||||
|
|
@ -242,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)
|
||||
|
|
@ -300,13 +299,14 @@ class UpdateTest(_common.TestCase):
|
|||
artfile = os.path.join(_common.RSRC, 'testart.jpg')
|
||||
_common.touch(artfile)
|
||||
self.album.set_art(artfile)
|
||||
self.album.store()
|
||||
os.remove(artfile)
|
||||
|
||||
def _update(self, query=(), album=False, move=False, reset_mtime=True):
|
||||
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):
|
||||
|
|
@ -333,7 +333,7 @@ class UpdateTest(_common.TestCase):
|
|||
mf.title = 'differentTitle'
|
||||
mf.save()
|
||||
self._update()
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertEqual(item.title, 'differentTitle')
|
||||
|
||||
def test_modified_metadata_moved(self):
|
||||
|
|
@ -341,7 +341,7 @@ class UpdateTest(_common.TestCase):
|
|||
mf.title = 'differentTitle'
|
||||
mf.save()
|
||||
self._update(move=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertTrue('differentTitle' in item.path)
|
||||
|
||||
def test_modified_metadata_not_moved(self):
|
||||
|
|
@ -349,7 +349,7 @@ class UpdateTest(_common.TestCase):
|
|||
mf.title = 'differentTitle'
|
||||
mf.save()
|
||||
self._update(move=False)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertTrue('differentTitle' not in item.path)
|
||||
|
||||
def test_modified_album_metadata_moved(self):
|
||||
|
|
@ -357,7 +357,7 @@ class UpdateTest(_common.TestCase):
|
|||
mf.album = 'differentAlbum'
|
||||
mf.save()
|
||||
self._update(move=True)
|
||||
item = self.lib.items().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertTrue('differentAlbum' in item.path)
|
||||
|
||||
def test_modified_album_metadata_art_moved(self):
|
||||
|
|
@ -376,10 +376,10 @@ 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().next()
|
||||
item = self.lib.items().get()
|
||||
self.assertEqual(item.title, 'full')
|
||||
|
||||
class PrintTest(_common.TestCase):
|
||||
|
|
|
|||
|
|
@ -7,11 +7,12 @@ from beetsplug.zero import ZeroPlugin
|
|||
|
||||
class ZeroPluginTest(unittest.TestCase):
|
||||
def test_no_patterns(self):
|
||||
v = {'comments' : 'test comment',
|
||||
'day' : 13,
|
||||
'month' : 3,
|
||||
'year' : 2012}
|
||||
i=Item(v)
|
||||
i = Item(
|
||||
comments='test comment',
|
||||
day=13,
|
||||
month=3,
|
||||
year=2012,
|
||||
)
|
||||
z = ZeroPlugin()
|
||||
z.debug = False
|
||||
z.fields = ['comments', 'month', 'day']
|
||||
|
|
@ -25,9 +26,10 @@ class ZeroPluginTest(unittest.TestCase):
|
|||
self.assertEqual(i.year, 2012)
|
||||
|
||||
def test_patterns(self):
|
||||
v = {'comments' : 'from lame collection, ripped by eac',
|
||||
'year' : 2012}
|
||||
i=Item(v)
|
||||
i = Item(
|
||||
comments='from lame collection, ripped by eac',
|
||||
year=2012,
|
||||
)
|
||||
z = ZeroPlugin()
|
||||
z.debug = False
|
||||
z.fields = ['comments', 'year']
|
||||
|
|
|
|||
Loading…
Reference in a new issue