Repository: beetbox/beets

Issue:
mbsync: Deal with albums that have multiple copies of the same recording
the current way mbsync plugin used to obtain track mapping list is to use the MusicBrainz recoding ID from each track, it's a workaround to handle "missing or extra tracks". This method is based on an assumption that for each MB release, there are no multiple tracks with same MB recording ID. It usually works, and in my case, only 4 out of 700+ albums disobey this assumption. But for this four albums, I have to fix them by tag track number by hand and re-import.

Considering it's called "mbsync", Why not make an assumption that track number in metadata is not corrupt and use it if possible, or fallback to MB recording ID way if it's corrupted(missing or extra track detected)



Discussion:
This is a particular problem for classical music, where the same recording can and will feature on multiple releases.

I suspect that #187 will also apply to this.

I'm not 100% sure I understand—is the problem that some albums have multiple iterations of the same recording? (That would be different from what @samdoshi mentioned, which is that the same recording appears twice on _different_ albums.) If so, you're totally right that we should use the track numbers to disambiguate case.

Also: for testing, can you share a link to a MusicBrainz entry for an affected album?

track 1.13 and track 2.17 of http://musicbrainz.org/release/a7641ce5-0c94-47be-86f2-203c2c2b5130

track 1 and track 14 of http://musicbrainz.org/release/a61e5ddc-1529-39c6-bf73-0286a527f1a2

I just had the same problem with an album, the [legacy edition of Ten](http://musicbrainz.org/release/06fc304e-d70a-4846-93e5-544d6aa618d6) by Pearl Jam. The legacy edition features both the original and remastered versions of the same 1991 album, which means each recording is featured twice in the release.

`beet import` handles this case well (with the track numbers already present beforehand), while `beet mbsync` will mess up the metadata, which is not desirable at all (`mbsync` should be kind of a "safe" operation to do after the importing step).

As I understand by the MusicBrainz [web service output](http://musicbrainz.org/ws/2/release/06fc304e-d70a-4846-93e5-544d6aa618d6?inc=recordings) for this album, there is a true track MBID for each track of a release. Why is the recording ID stored in the file tags instead of the track ID?

EDIT: it happened to me on another one: http://musicbrainz.org/release/d4ed7061-50b9-3348-acf0-cdeb6324549a. I think multiple tracks with the same recording are pretty common in best of albums and special editions.

Thanks for the second example, @ruippeixotog.

> Why is the recording ID stored in the file tags instead of the track ID?

Because it's usually more useful (i.e., it directly indicates the relevant metadata like the title, artist, length, etc., as well as reliably indicating when the same song appears on multiple albums). Having the track ID may be useful too, but we should not replace the recording ID.

Hmm, ok, it makes sense. I saw that Picard also stores the recording ID and not the track ID, and that one can't do much with the MusicBrainz's track ID. Then I agree with you that tracks like the examples above could be disambiguated by their track number, which should cause no problems as `mbsync` is supposed to be a post-import task.


Resolving patch:
diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py
--- a/beetsplug/mbsync.py
+++ b/beetsplug/mbsync.py
@@ -64,13 +64,29 @@ def mbsync_albums(lib, query, move, pretend, write):
             log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
             continue
 
+        # Construct an hash mapping recording MBIDs to their information. A
+        # release can have recording MBIDs that appear multiple times in the
+        # same release.
+        track_index = {}
+        for track_info in album_info.tracks:
+            if track_info.track_id in track_index:
+                track_index[track_info.track_id].append(track_info)
+            else:
+                track_index[track_info.track_id] = [track_info]
+
         # Construct a track mapping according to MBIDs. This should work
-        # for albums that have missing or extra tracks.
+        # for albums that have missing or extra tracks. If a mapping is
+        # ambiguous, the items' disc and track number need to match in order
+        # for an item to be mapped.
         mapping = {}
         for item in items:
-            for track_info in album_info.tracks:
-                if item.mb_trackid == track_info.track_id:
-                    mapping[item] = track_info
+            candidates = track_index.get(item.mb_trackid, [])
+            if len(candidates) == 1:
+                mapping[item] = candidates[0]
+                continue
+            for c in candidates:
+                if c.medium_index == item.track and c.medium == item.disc:
+                    mapping[item] = c
                     break
 
         # Apply.


Test patch: