mirror of
https://github.com/beetbox/beets.git
synced 2026-02-05 23:14:07 +01:00
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:
parent
42f2b2b096
commit
2f1ac61d4f
10 changed files with 53 additions and 28 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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__)
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue