track and album artist sort names (GH-25, GC-77)

Previously, there was just an "artist sort name" field -- now there's a
corresponding sort name for both track artists and album artists. I also made
the names shorter (artist_sort and albumartist_sort).
This commit is contained in:
Adrian Sampson 2012-03-25 17:02:52 -07:00
parent 42f2b2b096
commit 2f1ac61d4f
10 changed files with 53 additions and 28 deletions

View file

@ -125,13 +125,16 @@ def apply_metadata(items, album_info):
item.artist = track_info.artist
else:
item.artist = album_info.artist
if track_info.artist_sort_name:
item.artist_sort_name = track_info.artist_sort_name
elif album_info.artist_sort_name:
item.artist_sort_name = album_info.artist_sort_name
item.albumartist = album_info.artist
item.album = album_info.album
item.tracktotal = len(items)
# Artist sort names.
if track_info.artist_sort:
item.artist_sort = track_info.artist_sort
else:
item.artist_sort = album_info.artist_sort
item.albumartist_sort = album_info.artist_sort
# Release date.
if album_info.year:

View file

@ -36,14 +36,14 @@ class AlbumInfo(object):
- ``day``: release day
- ``label``: music label responsible for the release
- ``mediums``: the number of discs in this release
- ``artist_sort_name``: name of the release's artists for sorting
- ``artist_sort``: name of the release's artist for sorting
The fields up through ``tracks`` are required. The others are
optional and may be None.
"""
def __init__(self, album, album_id, artist, artist_id, tracks, asin=None,
albumtype=None, va=False, year=None, month=None, day=None,
label=None, mediums=None, artist_sort_name=None):
label=None, mediums=None, artist_sort=None):
self.album = album
self.album_id = album_id
self.artist = artist
@ -57,8 +57,7 @@ class AlbumInfo(object):
self.day = day
self.label = label
self.mediums = mediums
if artist_sort_name != '':
self.artist_sort_name = artist_sort_name
self.artist_sort = artist_sort
class TrackInfo(object):
"""Describes a canonical track present on a release. Appears as part
@ -71,14 +70,14 @@ class TrackInfo(object):
- ``length``: float: duration of the track in seconds
- ``medium``: the disc number this track appears on in the album
- ``medium_index``: the track's position on the disc
- ``artist_sort_name``: name of the release's artists for sorting
- ``artist_sort``: name of the track artist for sorting
Only ``title`` and ``track_id`` are required. The rest of the fields
may be None.
"""
def __init__(self, title, track_id, artist=None, artist_id=None,
length=None, medium=None, medium_index=None,
artist_sort_name=None):
artist_sort=None):
self.title = title
self.track_id = track_id
self.artist = artist
@ -86,7 +85,7 @@ class TrackInfo(object):
self.length = length
self.medium = medium
self.medium_index = medium_index
self.artist_sort_name = artist_sort_name
self.artist_sort = artist_sort
# Aggregation of sources.

View file

@ -55,15 +55,15 @@ def track_info(recording, medium=None, medium_index=None):
medium=medium,
medium_index=medium_index)
# Get the name of the track artist.
# Get the track artist credit.
if recording.get('artist-credit-phrase'):
info.artist = recording['artist-credit-phrase']
# Get the ID of the first artist.
# Get the ID and sort name of the first artist.
if 'artist-credit' in recording:
artist = recording['artist-credit'][0]['artist']
info.artist_id = artist['id']
info.artist_sort_name = artist['sort-name']
info.artist_sort = artist['sort-name']
if recording.get('length'):
info.length = int(recording['length'])/(1000.0)
@ -86,15 +86,12 @@ def album_info(release):
"""
# Get artist name using join phrases.
artist_parts = []
artist_sort_parts = []
for el in release['artist-credit']:
if isinstance(el, basestring):
artist_parts.append(el)
else:
artist_parts.append(el['artist']['name'])
artist_sort_parts.append(el['artist']['sort-name'])
artist_name = ''.join(artist_parts)
artist_sort_name = ', '.join(artist_sort_parts)
# Basic info.
track_infos = []
@ -115,7 +112,7 @@ def album_info(release):
release['artist-credit'][0]['artist']['id'],
track_infos,
mediums=len(release['medium-list']),
artist_sort_name = artist_sort_name,
artist_sort=release['artist-credit'][0]['artist']['sort-name'],
)
info.va = info.artist_id == VARIOUS_ARTISTS_ID
if 'asin' in release:

View file

@ -42,9 +42,10 @@ ITEM_FIELDS = [
('title', 'text', True, True),
('artist', 'text', True, True),
('artist_sort_name', 'text', True, True),
('artist_sort', 'text', True, True),
('album', 'text', True, True),
('albumartist', 'text', True, True),
('albumartist_sort', 'text', True, True),
('genre', 'text', True, True),
('composer', 'text', True, True),
('grouping', 'text', True, True),

View file

@ -743,11 +743,6 @@ class MediaFile(object):
mp3 = StorageStyle('TPE1'),
mp4 = StorageStyle("\xa9ART"),
etc = StorageStyle('artist'),
)
artist_sort_name = MediaField(
mp3 = StorageStyle('TSOP'),
mp4 = StorageStyle("soar"),
etc = StorageStyle('ARTISTSORT'),
)
album = MediaField(
mp3 = StorageStyle('TALB'),
@ -890,6 +885,16 @@ class MediaFile(object):
etc = [StorageStyle('label'),
StorageStyle('publisher')] # Traktor
)
artist_sort = MediaField(
mp3 = StorageStyle('TSOP'),
mp4 = StorageStyle("soar"),
etc = StorageStyle('ARTISTSORT'),
)
albumartist_sort = MediaField(
mp3 = StorageStyle('TXXX', id3_desc=u'ALBUMARTISTSORT'),
mp4 = StorageStyle("soaa"),
etc = StorageStyle('ALBUMARTISTSORT'),
)
# Album art.
art = ImageField()

View file

@ -10,6 +10,9 @@ Changelog
choices: skip the new music (the previous behavior), keep both, or remove the
old music. See the :ref:`guide-duplicates` section in the autotagging guide
for details.
* Artist **sort names** are now fetched from MusicBrainz. There are two new data
fields, ``artist_sort`` and ``albumartist_sort``, that contain sortable artist
names like "Beatles, The". Thanks to Paul Provost.
* New :doc:`/plugins/rdm`: Randomly select albums and tracks from your library.
Thanks to Philippe Mongeau.
* The :doc:`/plugins/mbcollection` by Jeffrey Aylesworth was added to the core

View file

@ -109,14 +109,16 @@ can be found definitively `in the source`_.) Note that plugins can add new (or
replace existing) template values (see :ref:`writing-plugins`).
.. _in the source:
http://code.google.com/p/beets/source/browse/beets/library.py#36
https://github.com/sampsyo/beets/blob/master/beets/library.py#L39
Ordinary metadata:
* title
* artist
* artist_sort
* album
* albumartist
* albumartist_sort
* genre
* composer
* grouping

Binary file not shown.

View file

@ -27,7 +27,11 @@ class MBAlbumInfoTest(unittest.TestCase):
'first-release-date': date_str,
},
'artist-credit': [
{'artist': {'name': 'ARTIST NAME', 'id': 'ARTIST ID'}}
{'artist': {
'name': 'ARTIST NAME',
'id': 'ARTIST ID',
'sort-name': 'ARTIST SORT NAME',
}}
],
'date': '3001',
'medium-list': [],
@ -163,6 +167,11 @@ class MBAlbumInfoTest(unittest.TestCase):
d = mb.album_info(release)
self.assertTrue(d.va)
def test_parse_artist_sort_name(self):
release = self._make_release(None)
d = mb.album_info(release)
self.assertEqual(d.artist_sort, 'ARTIST SORT NAME')
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View file

@ -195,7 +195,7 @@ correct_dicts = {
'disc': 0,
'disctotal': 0
},
# ID3 tag deleted with `mp3info -d`. Tests default values.
'empty': {
'title': u'',
@ -221,8 +221,14 @@ correct_dicts = {
'mb_artistid':u'',
'art': None,
'label': u'',
# Additional, non-iTunes fields.
'albumartist': u'',
'mb_albumartistid': u'',
'artist_sort': u'',
'albumartist_sort': u'',
},
# Full release date.
'date': {
'year': 1987,