Repository: beetbox/beets

Issue:
Artist Aliases being used instead of Artist Name
I just did an `mbsync` and found some weird behavior: A few of my albums have had their Album Artist set to an alias. 

Specifically, Bombay Bicycle Club is now "The Canals", which is an old name that artist used almost 10 years ago.

Looking at [the XML](http://musicbrainz.org/ws/2/release/fdb5dce3-e135-4d3e-ac52-b6cb0abb2e7c?inc=artists+artist-credits+recordings+aliases), it looks like beets is picking up the value from the artist `alias-list`. I assume this isn't by design?

I've seen this happen with a few other artists, such as Moby, which ends up being written out as "DJ Moby" (an alias present in [the XML](http://musicbrainz.org/ws/2/release/679841df-599d-402f-9f89-0d0bb6e94368?inc=artists+artist-credits+recordings+aliases)), so it's not new behavior as far as I can tell.

Looking at [the code](https://github.com/sampsyo/beets/blob/master/beets/autotag/mb.py#L90), the issue seems to be in the `_preferred_alias` function:

``` python
    # Search configured locales in order.
    for locale in config['import']['languages'].as_str_seq():
        # Find matching aliases for this locale.
        matches = [a for a in aliases if a['locale'] == locale]
        # Skip to the next locale if we have no matches
        if not matches:
            continue

        # Find the aliases that have the primary flag set.
        primaries = [a for a in matches if 'primary' in a]
        # Take the primary if we have it, otherwise take the first
        # match with the correct locale.
        if primaries:
            return primaries[0]
        else:
            return matches[0]
```

Perhaps MusicBrainz used to set `primary` on the alias entries, but it doesn't seem to be present in the albums I've checked. 



Discussion:
Interesting—yes, we do use artist aliases over the original name, which I believed until now was the right way to look at the MB data. From these examples, it looks like the "best" name for an artist is _not_ its alias, unless you need a language-specific name.

Here's some [more description of the primary flag](http://wiki.musicbrainz.org/Proposal:Alias_Types#Primary_flag), which seems to be missing on these examples.

I'm still confused about what the standard is for aliases. Why does Moby have "Moby" as an alias while Bombay Bicycle Club doesn't have its own current name? Is this just an error, something that should be corrected in the MB database? Some insight from an MB expert would be helpful here.

Aliases in musicbrainz serve different purposes. For [Bombay Bicycle Club](http://beta.musicbrainz.org/artist/0ae49abe-d6af-44fa-8ab0-b9ace5690e6f/aliases) the alias is the group’s old name. For [people whose names are written in a different script](http://beta.musicbrainz.org/artist/0ae49abe-d6af-44fa-8ab0-b9ace5690e6f/aliases) than latin it’s mostly transliterations. These transliteration have a locale and possibly a `primary` flag. Moby is kind of a weird case since the artist does not need an English alias.

I guess we should only use aliases if they have a locale attached and have the primary flag.

See the [musicbrainz documentation on aliases](http://beta.musicbrainz.org/doc/Style/Artist/Aliases) for more info.

Here's an entry for Tchaikovsky: http://musicbrainz.org/ws/2/release/b351260a-d07c-4c04-9692-b9eb1f24fde2?inc=artists+artist-credits+recordings+aliases

I thin @geigerzaehler is right, we should only use aliases if they have a locale & are primary. In this case, you would get `Pyotr Ilyich Tchaikovsky` for locale `en` for Tchaikovsky, as expected, but Moby and Bombay Bicycle Club would be fine.


Resolving patch:
diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py
--- a/beets/autotag/mb.py
+++ b/beets/autotag/mb.py
@@ -80,20 +80,13 @@ def _preferred_alias(aliases):
 
     # Search configured locales in order.
     for locale in config['import']['languages'].as_str_seq():
-        # Find matching aliases for this locale.
-        matches = [a for a in aliases if a['locale'] == locale]
+        # Find matching primary aliases for this locale.
+        matches = [a for a in aliases if a['locale'] == locale and 'primary' in a]
         # Skip to the next locale if we have no matches
         if not matches:
             continue
 
-        # Find the aliases that have the primary flag set.
-        primaries = [a for a in matches if 'primary' in a]
-        # Take the primary if we have it, otherwise take the first
-        # match with the correct locale.
-        if primaries:
-            return primaries[0]
-        else:
-            return matches[0]
+        return matches[0]
 
 def _flatten_artist_credit(credit):
     """Given a list representing an ``artist-credit`` block, flatten the


Test patch:
diff --git a/test/test_mb.py b/test/test_mb.py
--- a/test/test_mb.py
+++ b/test/test_mb.py
@@ -366,27 +366,28 @@ def test_two_artists(self):
 
     def test_alias(self):
         credit_dict = self._credit_dict()
-        self._add_alias(credit_dict, suffix='en', locale='en')
-        self._add_alias(credit_dict, suffix='en_GB', locale='en_GB')
+        self._add_alias(credit_dict, suffix='en', locale='en', primary=True)
+        self._add_alias(credit_dict, suffix='en_GB', locale='en_GB', primary=True)
         self._add_alias(credit_dict, suffix='fr', locale='fr')
         self._add_alias(credit_dict, suffix='fr_P', locale='fr', primary=True)
+        self._add_alias(credit_dict, suffix='pt_BR', locale='pt_BR')
 
         # test no alias
         config['import']['languages'] = ['']
         flat = mb._flatten_artist_credit([credit_dict])
         self.assertEqual(flat, ('NAME', 'SORT', 'CREDIT'))
 
-        # test en
+        # test en primary
         config['import']['languages'] = ['en']
         flat = mb._flatten_artist_credit([credit_dict])
         self.assertEqual(flat, ('ALIASen', 'ALIASSORTen', 'CREDIT'))
 
-        # test en_GB en
+        # test en_GB en primary
         config['import']['languages'] = ['en_GB', 'en']
         flat = mb._flatten_artist_credit([credit_dict])
         self.assertEqual(flat, ('ALIASen_GB', 'ALIASSORTen_GB', 'CREDIT'))
 
-        # test en en_GB
+        # test en en_GB primary
         config['import']['languages'] = ['en', 'en_GB']
         flat = mb._flatten_artist_credit([credit_dict])
         self.assertEqual(flat, ('ALIASen', 'ALIASSORTen', 'CREDIT'))
@@ -396,6 +397,11 @@ def test_alias(self):
         flat = mb._flatten_artist_credit([credit_dict])
         self.assertEqual(flat, ('ALIASfr_P', 'ALIASSORTfr_P', 'CREDIT'))
 
+        # test for not matching non-primary
+        config['import']['languages'] = ['pt_BR', 'fr']
+        flat = mb._flatten_artist_credit([credit_dict])
+        self.assertEqual(flat, ('ALIASfr_P', 'ALIASSORTfr_P', 'CREDIT'))
+
 def suite():
     return unittest.TestLoader().loadTestsFromName(__name__)