mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 21:14:19 +01:00
apply jonathan.buchanan's compilation patch from issue #48
This commit is contained in:
parent
3bfae3b78c
commit
342c360285
10 changed files with 303 additions and 107 deletions
|
|
@ -20,6 +20,7 @@ from collections import defaultdict
|
|||
from beets.autotag import mb
|
||||
import re
|
||||
from munkres import Munkres
|
||||
from musicbrainz2.model import VARIOUS_ARTISTS_ID
|
||||
from beets import library, mediafile, plugins
|
||||
import logging
|
||||
|
||||
|
|
@ -72,6 +73,8 @@ SD_PATTERNS = [
|
|||
(r'(, )?(pt\.|part) .+', 0.2),
|
||||
]
|
||||
|
||||
VARIOUS_ARTISTS_ID = VARIOUS_ARTISTS_ID.rsplit('/', 1)[1]
|
||||
|
||||
# Autotagging exceptions.
|
||||
class AutotagError(Exception):
|
||||
pass
|
||||
|
|
@ -350,9 +353,13 @@ def apply_metadata(items, info):
|
|||
"""Set the items' metadata to match the data given in info. The
|
||||
list of items must be ordered.
|
||||
"""
|
||||
for index, (item, track_data) in enumerate(zip(items, info['tracks'])):
|
||||
for index, (item, track_data) in enumerate(zip(items, info['tracks'])):
|
||||
# Album, artist, track count.
|
||||
item.artist = info['artist']
|
||||
if 'artist' in track_data:
|
||||
item.artist = track_data['artist']
|
||||
else:
|
||||
item.artist = info['artist']
|
||||
item.album_artist = info['artist']
|
||||
item.album = info['album']
|
||||
item.tracktotal = len(items)
|
||||
|
||||
|
|
@ -371,7 +378,15 @@ def apply_metadata(items, info):
|
|||
# MusicBrainz IDs.
|
||||
item.mb_trackid = track_data['id']
|
||||
item.mb_albumid = info['album_id']
|
||||
item.mb_artistid = info['artist_id']
|
||||
if 'artist_id' in track_data:
|
||||
item.mb_artistid = track_data['artist_id']
|
||||
else:
|
||||
item.mb_artistid = info['artist_id']
|
||||
item.mb_albumartistid = info['artist_id']
|
||||
item.mb_albumtype = info['albumtype']
|
||||
|
||||
# Compilation flag.
|
||||
item.comp = (info['artist_id'] == VARIOUS_ARTISTS_ID)
|
||||
|
||||
def match_by_id(items):
|
||||
"""If the items are tagged with a MusicBrainz album ID, returns an
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import re
|
|||
import time
|
||||
import datetime
|
||||
import musicbrainz2.webservice as mbws
|
||||
from musicbrainz2.model import Release
|
||||
from threading import Lock
|
||||
|
||||
SEARCH_LIMIT = 10
|
||||
|
|
@ -36,6 +37,20 @@ SPECIAL_CASE_ARTISTS = {
|
|||
'!!!': 'f26c72d3-e52c-467b-b651-679c73d8e1a7',
|
||||
}
|
||||
|
||||
RELEASE_TYPES = [
|
||||
Release.TYPE_ALBUM,
|
||||
Release.TYPE_SINGLE,
|
||||
Release.TYPE_EP,
|
||||
Release.TYPE_COMPILATION,
|
||||
Release.TYPE_SOUNDTRACK,
|
||||
Release.TYPE_SPOKENWORD,
|
||||
Release.TYPE_INTERVIEW,
|
||||
Release.TYPE_AUDIOBOOK,
|
||||
Release.TYPE_LIVE,
|
||||
Release.TYPE_REMIX,
|
||||
Release.TYPE_OTHER
|
||||
]
|
||||
|
||||
# MusicBrainz requires that a client does not query the server more
|
||||
# than once a second. This function enforces that limit using a
|
||||
# module-global variable to keep track of the last time a query was
|
||||
|
|
@ -153,8 +168,15 @@ def release_dict(release, tracks=None):
|
|||
'artist': release.artist.name,
|
||||
'artist_id': release.artist.id.rsplit('/', 1)[1],
|
||||
'asin': release.asin,
|
||||
'albumtype': '',
|
||||
}
|
||||
|
||||
# Release type not always populated.
|
||||
for type in release.types:
|
||||
if type in RELEASE_TYPES:
|
||||
out['albumtype'] = type.split('#')[1].lower()
|
||||
break
|
||||
|
||||
# Release date.
|
||||
try:
|
||||
date_str = release.getEarliestReleaseDate()
|
||||
|
|
@ -176,6 +198,11 @@ def release_dict(release, tracks=None):
|
|||
for track in tracks:
|
||||
t = {'title': track.title,
|
||||
'id': track.id.rsplit('/', 1)[1]}
|
||||
if track.artist is not None:
|
||||
# Track artists will only be present for releases with
|
||||
# multiple artists.
|
||||
t['artist'] = track.artist.name
|
||||
t['artist_id'] = track.artist.id.rsplit('/', 1)[1]
|
||||
if track.duration is not None:
|
||||
# Duration not always present.
|
||||
t['length'] = track.duration/(1000.0)
|
||||
|
|
|
|||
147
beets/library.py
147
beets/library.py
|
|
@ -38,26 +38,29 @@ ITEM_FIELDS = [
|
|||
('path', 'blob', False, False),
|
||||
('album_id', 'int', False, False),
|
||||
|
||||
('title', 'text', True, True),
|
||||
('artist', 'text', True, True),
|
||||
('album', 'text', True, True),
|
||||
('genre', 'text', True, True),
|
||||
('composer', 'text', True, True),
|
||||
('grouping', 'text', True, True),
|
||||
('year', 'int', True, True),
|
||||
('month', 'int', True, True),
|
||||
('day', 'int', True, True),
|
||||
('track', 'int', True, True),
|
||||
('tracktotal', 'int', True, True),
|
||||
('disc', 'int', True, True),
|
||||
('disctotal', 'int', True, True),
|
||||
('lyrics', 'text', True, True),
|
||||
('comments', 'text', True, True),
|
||||
('bpm', 'int', True, True),
|
||||
('comp', 'bool', True, True),
|
||||
('mb_trackid', 'text', True, True),
|
||||
('mb_albumid', 'text', True, True),
|
||||
('mb_artistid', 'text', True, True),
|
||||
('title', 'text', True, True),
|
||||
('artist', 'text', True, True),
|
||||
('album', 'text', True, True),
|
||||
('album_artist', 'text', True, True),
|
||||
('genre', 'text', True, True),
|
||||
('composer', 'text', True, True),
|
||||
('grouping', 'text', True, True),
|
||||
('year', 'int', True, True),
|
||||
('month', 'int', True, True),
|
||||
('day', 'int', True, True),
|
||||
('track', 'int', True, True),
|
||||
('tracktotal', 'int', True, True),
|
||||
('disc', 'int', True, True),
|
||||
('disctotal', 'int', True, True),
|
||||
('lyrics', 'text', True, True),
|
||||
('comments', 'text', True, True),
|
||||
('bpm', 'int', True, True),
|
||||
('comp', 'bool', True, True),
|
||||
('mb_trackid', 'text', True, True),
|
||||
('mb_albumid', 'text', True, True),
|
||||
('mb_artistid', 'text', True, True),
|
||||
('mb_albumartistid', 'text', True, True),
|
||||
('mb_albumtype', 'text', True, True),
|
||||
|
||||
('length', 'real', False, True),
|
||||
('bitrate', 'int', False, True),
|
||||
|
|
@ -68,31 +71,38 @@ ITEM_KEYS_META = [f[0] for f in ITEM_FIELDS if f[3]]
|
|||
ITEM_KEYS = [f[0] for f in ITEM_FIELDS]
|
||||
|
||||
# Database fields for the "albums" table.
|
||||
# The third entry in each tuple indicates whether the field reflects an
|
||||
# identically-named field in the items table.
|
||||
# The third entry in each tuple indicates whether the field reflects a
|
||||
# field in the items table.
|
||||
ALBUM_FIELDS = [
|
||||
('id', 'integer primary key', False),
|
||||
('artpath', 'blob', False),
|
||||
('id', 'integer primary key', False),
|
||||
('artpath', 'blob', False),
|
||||
|
||||
('artist', 'text', True),
|
||||
('album', 'text', True),
|
||||
('genre', 'text', True),
|
||||
('year', 'int', True),
|
||||
('month', 'int', True),
|
||||
('day', 'int', True),
|
||||
('tracktotal', 'int', True),
|
||||
('disctotal', 'int', True),
|
||||
('comp', 'bool', True),
|
||||
('mb_albumid', 'text', True),
|
||||
('mb_artistid', 'text', True),
|
||||
('artist', 'text', True),
|
||||
('album', 'text', True),
|
||||
('genre', 'text', True),
|
||||
('year', 'int', True),
|
||||
('month', 'int', True),
|
||||
('day', 'int', True),
|
||||
('tracktotal', 'int', True),
|
||||
('disctotal', 'int', True),
|
||||
('comp', 'bool', True),
|
||||
('mb_albumid', 'text', True),
|
||||
('mb_artistid', 'text', True),
|
||||
('mb_albumtype', 'text', True),
|
||||
]
|
||||
ALBUM_KEYS = [f[0] for f in ALBUM_FIELDS]
|
||||
ALBUM_KEYS_ITEM = [f[0] for f in ALBUM_FIELDS if f[2]]
|
||||
# Mapping between album field names and item field names, where they
|
||||
# differ.
|
||||
ALBUM_KEYS_ITEM_MAP = {
|
||||
'artist': 'album_artist',
|
||||
'mb_artistid': 'mb_albumartistid',
|
||||
}
|
||||
|
||||
# Default search fields for various granularities.
|
||||
ARTIST_DEFAULT_FIELDS = ('artist',)
|
||||
ALBUM_DEFAULT_FIELDS = ARTIST_DEFAULT_FIELDS + ('album', 'genre')
|
||||
ITEM_DEFAULT_FIELDS = ALBUM_DEFAULT_FIELDS + ('title', 'comments')
|
||||
ITEM_DEFAULT_FIELDS = ALBUM_DEFAULT_FIELDS + ('album_artist', 'title', 'comments')
|
||||
|
||||
# Logger.
|
||||
log = logging.getLogger('beets')
|
||||
|
|
@ -708,7 +718,7 @@ class BaseLibrary(object):
|
|||
item = specimens[k]
|
||||
record = {}
|
||||
for key in ALBUM_KEYS_ITEM:
|
||||
record[key] = getattr(item, key)
|
||||
record[key] = getattr(item, ALBUM_KEYS_ITEM_MAP.get(key, key))
|
||||
yield BaseAlbum(self, record)
|
||||
|
||||
def items(self, artist=None, album=None, title=None, query=None):
|
||||
|
|
@ -762,10 +772,10 @@ class BaseAlbum(object):
|
|||
self._record[key] = value
|
||||
# Modify items.
|
||||
if key in ALBUM_KEYS_ITEM:
|
||||
items = self._library.items(artist=self.artist,
|
||||
items = self._library.items(album_artist=self.artist,
|
||||
album=self.album)
|
||||
for item in items:
|
||||
setattr(item, key, value)
|
||||
setattr(item, ALBUM_KEYS_ITEM_MAP.get(key, key), value)
|
||||
self._library.store(item)
|
||||
else:
|
||||
object.__setattr__(self, key, value)
|
||||
|
|
@ -776,7 +786,7 @@ class BaseAlbum(object):
|
|||
items = self._library.items(artist=self.artist, album=self.album)
|
||||
item = iter(items).next()
|
||||
for key in ALBUM_KEYS_ITEM:
|
||||
self._record[key] = getattr(item, key)
|
||||
self._record[key] = getattr(item, ALBUM_KEYS_ITEM_MAP.get(key, key))
|
||||
|
||||
|
||||
# Concrete DB-backed library.
|
||||
|
|
@ -785,13 +795,17 @@ class Library(BaseLibrary):
|
|||
"""A music library using an SQLite database as a metadata store."""
|
||||
def __init__(self, path='library.blb',
|
||||
directory='~/Music',
|
||||
path_format='$artist/$album/$track $title',
|
||||
path_formats=None,
|
||||
art_filename='cover',
|
||||
item_fields=ITEM_FIELDS,
|
||||
album_fields=ALBUM_FIELDS):
|
||||
self.path = _bytestring_path(path)
|
||||
self.directory = _bytestring_path(directory)
|
||||
self.path_format = path_format
|
||||
if path_formats is None:
|
||||
path_formats = {'default': '$artist/$album/$track $title'}
|
||||
elif isinstance(path_formats, basestring):
|
||||
path_formats = {'default': path_formats}
|
||||
self.path_formats = path_formats
|
||||
self.art_filename = _bytestring_path(art_filename)
|
||||
|
||||
self.conn = sqlite3.connect(self.path)
|
||||
|
|
@ -836,13 +850,34 @@ class Library(BaseLibrary):
|
|||
|
||||
self.conn.executescript(setup_sql)
|
||||
self.conn.commit()
|
||||
|
||||
def _sanitize_for_path(self, value, pathmod, key=None):
|
||||
"""Sanitize the value for inclusion in a path: replace separators
|
||||
with _, etc.
|
||||
"""
|
||||
if isinstance(value, basestring):
|
||||
for sep in (pathmod.sep, pathmod.altsep):
|
||||
if sep:
|
||||
value = value.replace(sep, '_')
|
||||
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
|
||||
# pad with zeros
|
||||
value = '%02i' % value
|
||||
else:
|
||||
value = str(value)
|
||||
return value
|
||||
|
||||
def destination(self, item, pathmod=None):
|
||||
"""Returns the path in the library directory designated for item
|
||||
item (i.e., where the file ought to be).
|
||||
"""
|
||||
pathmod = pathmod or os.path
|
||||
subpath_tmpl = Template(self.path_format)
|
||||
|
||||
# Use a path format based on the album type, if available.
|
||||
if item.comp and 'comp' in self.path_formats:
|
||||
path_format = self.path_formats['comp']
|
||||
else:
|
||||
path_format = self.path_formats['default']
|
||||
subpath_tmpl = Template(path_format)
|
||||
|
||||
# Get the item's Album if it has one.
|
||||
album = self.get_album(item)
|
||||
|
|
@ -858,19 +893,11 @@ class Library(BaseLibrary):
|
|||
else:
|
||||
# From Item.
|
||||
value = getattr(item, key)
|
||||
|
||||
# Sanitize the value for inclusion in a path: replace
|
||||
# separators with _, etc.
|
||||
if isinstance(value, basestring):
|
||||
for sep in (pathmod.sep, pathmod.altsep):
|
||||
if sep:
|
||||
value = value.replace(sep, '_')
|
||||
elif key in ('track', 'tracktotal', 'disc', 'disctotal'):
|
||||
# pad with zeros
|
||||
value = '%02i' % value
|
||||
else:
|
||||
value = str(value)
|
||||
mapping[key] = value
|
||||
mapping[key] = self._sanitize_for_path(value, pathmod, key)
|
||||
|
||||
# Use the track's artist if it differs
|
||||
if album is not None and item.artist != album.artist:
|
||||
mapping['artist'] = self._sanitize_for_path(item.artist, pathmod)
|
||||
|
||||
# Perform substitution.
|
||||
subpath = subpath_tmpl.substitute(mapping)
|
||||
|
|
@ -1058,7 +1085,8 @@ class Library(BaseLibrary):
|
|||
sql = 'INSERT INTO albums (%s) VALUES (%s)' % \
|
||||
(', '.join(ALBUM_KEYS_ITEM),
|
||||
', '.join(['?'] * len(ALBUM_KEYS_ITEM)))
|
||||
subvals = [getattr(items[0], key) for key in ALBUM_KEYS_ITEM]
|
||||
subvals = [getattr(items[0], ALBUM_KEYS_ITEM_MAP.get(key, key))
|
||||
for key in ALBUM_KEYS_ITEM]
|
||||
c = self.conn.execute(sql, subvals)
|
||||
album_id = c.lastrowid
|
||||
|
||||
|
|
@ -1066,7 +1094,8 @@ class Library(BaseLibrary):
|
|||
record = {}
|
||||
for key in ALBUM_KEYS:
|
||||
if key in ALBUM_KEYS_ITEM:
|
||||
record[key] = getattr(items[0], key)
|
||||
record[key] = getattr(items[0],
|
||||
ALBUM_KEYS_ITEM_MAP.get(key, key))
|
||||
else:
|
||||
# Non-item fields default to None.
|
||||
record[key] = None
|
||||
|
|
@ -1118,7 +1147,7 @@ class Album(BaseAlbum):
|
|||
# Possibly make modification on items as well.
|
||||
if key in ALBUM_KEYS_ITEM:
|
||||
for item in self.items():
|
||||
setattr(item, key, value)
|
||||
setattr(item, ALBUM_KEYS_ITEM_MAP.get(key, key), value)
|
||||
self._library.store(item)
|
||||
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -657,8 +657,17 @@ class MediaFile(object):
|
|||
as_type = bool),
|
||||
etc = StorageStyle('compilation')
|
||||
)
|
||||
|
||||
# MusicBrainz IDs.
|
||||
|
||||
# Album Artist
|
||||
album_artist = MediaField(
|
||||
mp3 = StorageStyle('TXXX', id3_desc='Album Artist'),
|
||||
mp4 = StorageStyle(
|
||||
'----:com.apple.iTunes:Album Artist',
|
||||
as_type=str),
|
||||
etc = StorageStyle('album_artist')
|
||||
)
|
||||
|
||||
# MusicBrainz fields
|
||||
mb_trackid = MediaField(
|
||||
mp3 = StorageStyle('UFID:http://musicbrainz.org',
|
||||
list_elem = False),
|
||||
|
|
@ -673,14 +682,28 @@ class MediaFile(object):
|
|||
'----:com.apple.iTunes:MusicBrainz Album Id',
|
||||
as_type=str),
|
||||
etc = StorageStyle('musicbrainz_albumid')
|
||||
)
|
||||
)
|
||||
mb_artistid = MediaField(
|
||||
mp3 = StorageStyle('TXXX', id3_desc='MusicBrainz Artist Id'),
|
||||
mp4 = StorageStyle(
|
||||
'----:com.apple.iTunes:MusicBrainz Artist Id',
|
||||
as_type=str),
|
||||
etc = StorageStyle('musicbrainz_artistid')
|
||||
)
|
||||
)
|
||||
mb_albumartistid = MediaField(
|
||||
mp3 = StorageStyle('TXXX', id3_desc='MusicBrainz Album Artist Id'),
|
||||
mp4 = StorageStyle(
|
||||
'----:com.apple.iTunes:MusicBrainz Album Artist Id',
|
||||
as_type=str),
|
||||
etc = StorageStyle('musicbrainz_albumartistid')
|
||||
)
|
||||
mb_albumtype = MediaField(
|
||||
mp3 = StorageStyle('TXXX', id3_desc='MusicBrainz Album Type'),
|
||||
mp4 = StorageStyle(
|
||||
'----:com.apple.iTunes:MusicBrainz Album Type',
|
||||
as_type=str),
|
||||
etc = StorageStyle('musicbrainz_albumtype')
|
||||
)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
|
|
|
|||
|
|
@ -436,13 +436,20 @@ def main():
|
|||
config_val(config, 'beets', 'library', DEFAULT_LIBRARY)
|
||||
directory = options.directory or \
|
||||
config_val(config, 'beets', 'directory', DEFAULT_DIRECTORY)
|
||||
path_format = options.path_format or \
|
||||
config_val(config, 'beets', 'path_format', DEFAULT_PATH_FORMAT)
|
||||
if options.path_format:
|
||||
# If given, -p overrides all path format settings
|
||||
path_formats = {'default': options.path_format}
|
||||
else:
|
||||
path_formats = {
|
||||
'default': config_val(config, 'beets', 'path_format', DEFAULT_PATH_FORMAT)
|
||||
}
|
||||
if config.has_section('paths'):
|
||||
path_formats.update(config.items('paths'))
|
||||
art_filename = \
|
||||
config_val(config, 'beets', 'art_filename', DEFAULT_ART_FILENAME)
|
||||
lib = library.Library(os.path.expanduser(libpath),
|
||||
directory,
|
||||
path_format,
|
||||
path_formats,
|
||||
art_filename)
|
||||
|
||||
# Configure the logger.
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ def _reopen_lib(lib):
|
|||
return library.Library(
|
||||
lib.path,
|
||||
lib.directory,
|
||||
lib.path_format,
|
||||
lib.path_formats,
|
||||
lib.art_filename,
|
||||
)
|
||||
else:
|
||||
|
|
@ -482,7 +482,7 @@ def apply_choices(lib, copy, write, art, delete):
|
|||
artpath = beets.autotag.art.art_for_album(info)
|
||||
if artpath:
|
||||
albuminfo.set_art(artpath)
|
||||
|
||||
|
||||
# Write the database after each album.
|
||||
lib.save()
|
||||
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ class ApplyTest(unittest.TestCase):
|
|||
'album': 'albumNew',
|
||||
'album_id': '7edb51cb-77d6-4416-a23c-3a8c2994a2c7',
|
||||
'artist_id': 'a6623d39-2d8e-4f70-8242-0a9553b91e50',
|
||||
'albumtype': 'album',
|
||||
}
|
||||
|
||||
def test_titles_applied(self):
|
||||
|
|
@ -242,6 +243,87 @@ class ApplyTest(unittest.TestCase):
|
|||
self.assertEqual(item.mb_artistid,
|
||||
'a6623d39-2d8e-4f70-8242-0a9553b91e50')
|
||||
|
||||
def test_comp_flag(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].mb_albumtype, 'album')
|
||||
self.assertEqual(self.items[1].mb_albumtype, 'album')
|
||||
self.assertFalse(self.items[0].comp)
|
||||
self.assertFalse(self.items[1].comp)
|
||||
|
||||
class ApplyCompilationTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.items = []
|
||||
self.items.append(Item({}))
|
||||
self.items.append(Item({}))
|
||||
trackinfo = []
|
||||
trackinfo.append({
|
||||
'title': 'oneNew',
|
||||
'id': 'dfa939ec-118c-4d0f-84a0-60f3d1e6522c',
|
||||
'artist': 'artistOneNew',
|
||||
'artist_id': 'a05686fc-9db2-4c23-b99e-77f5db3e5282',
|
||||
})
|
||||
trackinfo.append({
|
||||
'title': 'twoNew',
|
||||
'id': '40130ed1-a27c-42fd-a328-1ebefb6caef4',
|
||||
'artist': 'artistTwoNew',
|
||||
'artist_id': '80b3cf5e-18fe-4c59-98c7-e5bb87210710',
|
||||
})
|
||||
self.info = {
|
||||
'tracks': trackinfo,
|
||||
'artist': 'variousNew',
|
||||
'album': 'albumNew',
|
||||
'album_id': '3b69ea40-39b8-487f-8818-04b6eff8c21a',
|
||||
'artist_id': '89ad4ac3-39f7-470e-963a-56509c546377',
|
||||
'albumtype': 'compilation',
|
||||
}
|
||||
|
||||
def test_titles_applied(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].title, 'oneNew')
|
||||
self.assertEqual(self.items[1].title, 'twoNew')
|
||||
|
||||
def test_album_and_artist_applied_to_all(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].album, 'albumNew')
|
||||
self.assertEqual(self.items[1].album, 'albumNew')
|
||||
self.assertEqual(self.items[0].album_artist, 'variousNew')
|
||||
self.assertEqual(self.items[1].album_artist, 'variousNew')
|
||||
self.assertEqual(self.items[0].artist, 'artistOneNew')
|
||||
self.assertEqual(self.items[1].artist, 'artistTwoNew')
|
||||
|
||||
def test_track_index_applied(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].track, 1)
|
||||
self.assertEqual(self.items[1].track, 2)
|
||||
|
||||
def test_track_total_applied(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].tracktotal, 2)
|
||||
self.assertEqual(self.items[1].tracktotal, 2)
|
||||
|
||||
def test_mb_trackid_applied(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].mb_trackid,
|
||||
'dfa939ec-118c-4d0f-84a0-60f3d1e6522c')
|
||||
self.assertEqual(self.items[1].mb_trackid,
|
||||
'40130ed1-a27c-42fd-a328-1ebefb6caef4')
|
||||
|
||||
def test_mb_albumid_and_artistid_applied(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].mb_albumid, '3b69ea40-39b8-487f-8818-04b6eff8c21a')
|
||||
self.assertEqual(self.items[1].mb_albumid, '3b69ea40-39b8-487f-8818-04b6eff8c21a')
|
||||
self.assertEqual(self.items[0].mb_albumartistid, '89ad4ac3-39f7-470e-963a-56509c546377')
|
||||
self.assertEqual(self.items[1].mb_albumartistid, '89ad4ac3-39f7-470e-963a-56509c546377')
|
||||
self.assertEqual(self.items[0].mb_artistid, 'a05686fc-9db2-4c23-b99e-77f5db3e5282')
|
||||
self.assertEqual(self.items[1].mb_artistid, '80b3cf5e-18fe-4c59-98c7-e5bb87210710')
|
||||
|
||||
def test_comp_flag(self):
|
||||
autotag.apply_metadata(self.items, self.info)
|
||||
self.assertEqual(self.items[0].mb_albumtype, 'compilation')
|
||||
self.assertEqual(self.items[1].mb_albumtype, 'compilation')
|
||||
self.assertTrue(self.items[0].comp)
|
||||
self.assertTrue(self.items[1].comp)
|
||||
|
||||
class StringDistanceTest(unittest.TestCase):
|
||||
def test_equal_strings(self):
|
||||
dist = autotag.string_dist('Some String', 'Some String')
|
||||
|
|
|
|||
|
|
@ -28,31 +28,33 @@ def lib(): return beets.library.Library('rsrc' + os.sep + 'test.blb')
|
|||
def boracay(l): return beets.library.Item(l.conn.execute('select * from items '
|
||||
'where id=3').fetchone())
|
||||
def item(): return beets.library.Item({
|
||||
'title': u'the title',
|
||||
'artist': u'the 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',
|
||||
'length': 60.0,
|
||||
'bitrate': 128000,
|
||||
'format': 'FLAC',
|
||||
'mb_trackid': 'someID-1',
|
||||
'mb_albumid': 'someID-2',
|
||||
'mb_artistid': 'someID-3',
|
||||
'album_id': None,
|
||||
'title': u'the title',
|
||||
'artist': u'the artist',
|
||||
'album_artist': 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',
|
||||
'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,
|
||||
})
|
||||
np = beets.library._normpath
|
||||
|
||||
|
|
@ -161,17 +163,17 @@ class DestinationTest(unittest.TestCase):
|
|||
|
||||
def test_directory_works_with_trailing_slash(self):
|
||||
self.lib.directory = 'one/'
|
||||
self.lib.path_format = 'two'
|
||||
self.lib.path_formats = {'default': 'two'}
|
||||
self.assertEqual(self.lib.destination(self.i), np('one/two'))
|
||||
|
||||
def test_directory_works_without_trailing_slash(self):
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_format = 'two'
|
||||
self.lib.path_formats = {'default': 'two'}
|
||||
self.assertEqual(self.lib.destination(self.i), np('one/two'))
|
||||
|
||||
def test_destination_substitues_metadata_values(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_format = '$album/$artist $title'
|
||||
self.lib.path_formats = {'default': '$album/$artist $title'}
|
||||
self.i.title = 'three'
|
||||
self.i.artist = 'two'
|
||||
self.i.album = 'one'
|
||||
|
|
@ -180,15 +182,15 @@ class DestinationTest(unittest.TestCase):
|
|||
|
||||
def test_destination_preserves_extension(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_format = '$title'
|
||||
self.lib.path_formats = {'default': '$title'}
|
||||
self.i.path = 'hey.audioFormat'
|
||||
self.assertEqual(self.lib.destination(self.i),
|
||||
np('base/the title.audioFormat'))
|
||||
|
||||
def test_destination_pads_some_indices(self):
|
||||
self.lib.directory = 'base'
|
||||
self.lib.path_format = '$track $tracktotal ' \
|
||||
'$disc $disctotal $bpm $year'
|
||||
self.lib.path_formats = {'default': '$track $tracktotal ' \
|
||||
'$disc $disctotal $bpm $year'}
|
||||
self.i.track = 1
|
||||
self.i.tracktotal = 2
|
||||
self.i.disc = 3
|
||||
|
|
@ -260,7 +262,7 @@ class DestinationTest(unittest.TestCase):
|
|||
self.assertEqual(p, u'-')
|
||||
|
||||
def test_path_with_format(self):
|
||||
self.lib.path_format = '$artist/$album ($format)'
|
||||
self.lib.path_formats = {'default': '$artist/$album ($format)'}
|
||||
p = self.lib.destination(self.i)
|
||||
self.assert_('(FLAC)' in p)
|
||||
|
||||
|
|
@ -268,9 +270,16 @@ class DestinationTest(unittest.TestCase):
|
|||
i1, i2 = item(), item()
|
||||
self.lib.add_album([i1, i2])
|
||||
i1.year, i2.year = 2009, 2010
|
||||
self.lib.path_format = '$album ($year)/$track $title'
|
||||
self.lib.path_formats = {'default': '$album ($year)/$track $title'}
|
||||
dest1, dest2 = self.lib.destination(i1), self.lib.destination(i2)
|
||||
self.assertEqual(os.path.dirname(dest1), os.path.dirname(dest2))
|
||||
|
||||
def test_comp_path(self):
|
||||
self.i.comp = True
|
||||
self.lib.directory = 'one'
|
||||
self.lib.path_formats = {'default': 'two',
|
||||
'comp': 'three'}
|
||||
self.assertEqual(self.lib.destination(self.i), np('one/three'))
|
||||
|
||||
def test_syspath_windows_format(self):
|
||||
path = ntpath.join('a', 'b', 'c')
|
||||
|
|
@ -370,7 +379,8 @@ class AlbumInfoTest(unittest.TestCase):
|
|||
|
||||
def test_albuminfo_reflects_metadata(self):
|
||||
ai = self.lib.get_album(self.i)
|
||||
self.assertEqual(ai.artist, self.i.artist)
|
||||
self.assertEqual(ai.mb_artistid, self.i.mb_albumartistid)
|
||||
self.assertEqual(ai.artist, self.i.album_artist)
|
||||
self.assertEqual(ai.album, self.i.album)
|
||||
self.assertEqual(ai.year, self.i.year)
|
||||
|
||||
|
|
@ -523,7 +533,6 @@ class PathStringTest(unittest.TestCase):
|
|||
alb = self.lib.get_album(alb.id)
|
||||
self.assert_(isinstance(alb.artpath, str))
|
||||
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class MoveTest(unittest.TestCase):
|
|||
# set up the destination
|
||||
self.libdir = join('rsrc', 'testlibdir')
|
||||
self.lib.directory = self.libdir
|
||||
self.lib.path_format = join('$artist', '$album', '$title')
|
||||
self.lib.path_formats = {'default': join('$artist', '$album', '$title')}
|
||||
self.i.artist = 'one'
|
||||
self.i.album = 'two'
|
||||
self.i.title = 'three'
|
||||
|
|
@ -129,6 +129,7 @@ class AlbumFileTest(unittest.TestCase):
|
|||
def setUp(self):
|
||||
# Make library and item.
|
||||
self.lib = beets.library.Library(':memory:')
|
||||
self.lib.path_formats = {'default': join('$album_artist', '$album', '$title')}
|
||||
self.libdir = os.path.join('rsrc', 'testlibdir')
|
||||
self.lib.directory = self.libdir
|
||||
self.i = item()
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ class MBReleaseDictTest(unittest.TestCase):
|
|||
release = musicbrainz2.model.Release()
|
||||
release.title = 'ALBUM TITLE'
|
||||
release.id = 'domain/ALBUM ID'
|
||||
release.addType(musicbrainz2.model.Release.TYPE_ALBUM)
|
||||
release.addType(musicbrainz2.model.Release.TYPE_OFFICIAL)
|
||||
release.artist = musicbrainz2.model.Artist()
|
||||
release.artist.name = 'ARTIST NAME'
|
||||
release.artist.id = 'domain/ARTIST ID'
|
||||
|
|
@ -84,6 +86,7 @@ class MBReleaseDictTest(unittest.TestCase):
|
|||
d = mb.release_dict(release)
|
||||
self.assertEqual(d['album'], 'ALBUM TITLE')
|
||||
self.assertEqual(d['album_id'], 'ALBUM ID')
|
||||
self.assertEqual(d['albumtype'], 'album')
|
||||
self.assertEqual(d['artist'], 'ARTIST NAME')
|
||||
self.assertEqual(d['artist_id'], 'ARTIST ID')
|
||||
self.assertEqual(d['year'], 1984)
|
||||
|
|
|
|||
Loading…
Reference in a new issue