Repository: fennekki/cdparacord

Issue:
For multi-cd releases, only the first CD is considered when fetching sources
For example, the Outkast double album Speakerboxxx/The Love Below contains two CDs (labeled, appropriately enough, Speakerboxxx and The Love Below). The first CD has 19 tracks and the second one 20.

If you try ripping the first CD, everything works out fine and MusicBrainz data is correctly fetched. However, if you're ripping the *second* CD, you get this warning:
```
Warning: Source MusicBrainz dropped for wrong track count (Got 19, 20 expected)
```

This seems to suggest only the first CD is considered when fetching sources, making it currently impossible to get MusicBrainz data properly for multi-CD releases.

Discussion:

Patch:
diff --git a/cdparacord/albumdata.py b/cdparacord/albumdata.py
index 757f422..37a9cde 100644
--- a/cdparacord/albumdata.py
+++ b/cdparacord/albumdata.py
@@ -124,13 +124,15 @@ class Albumdata:
         """Convert MusicBrainz cdstub to albumdata."""
         albumdata = {}
 
-        albumdata['source'] = 'MusicBrainz'
+        albumdata['source'] = 'MusicBrainz CD stub'
         albumdata['title'] = cdstub['title']
         # Sometimes cd stubs don't have date. We can just put an empty
         # value there.
         albumdata['date'] = cdstub.get('date', '')
         albumdata['albumartist'] = cdstub['artist']
         albumdata['tracks'] = []
+        albumdata['cd_number'] = 1
+        albumdata['cd_count'] = 1
 
         for track in cdstub['track-list']:
             albumdata['tracks'].append({
@@ -147,26 +149,30 @@ class Albumdata:
 
         releases = disc['release-list']
         for release in releases:
-            albumdata = {}
-
-            albumdata['source'] = 'MusicBrainz'
-            albumdata['title'] = release['title']
-            # Sometimes albumdata doesn't seem to have date. In those
-            # cases, we can just put an empty value there.
-            albumdata['date'] = release.get('date', '')
-            albumdata['tracks'] = []
-            albumartist = release['artist-credit-phrase']
-            albumdata['albumartist'] = albumartist
-
-            medium = release['medium-list'][0]
-            for track in medium['track-list']:
-                recording = track['recording']
-                albumdata['tracks'].append({
-                    'title': recording['title'],
-                    'artist': recording['artist-credit-phrase']
-                })
-
-            result.append(albumdata)
+            cd_number = 0
+            for medium in release['medium-list']:
+                cd_number +=1
+                albumdata = {}
+
+                albumdata['source'] = 'MusicBrainz'
+                albumdata['title'] = release['title']
+                # Sometimes albumdata doesn't seem to have date. In those
+                # cases, we can just put an empty value there.
+                albumdata['date'] = release.get('date', '')
+                albumdata['tracks'] = []
+                albumartist = release['artist-credit-phrase']
+                albumdata['albumartist'] = albumartist
+                albumdata['cd_number'] = cd_number
+                albumdata['cd_count'] = len(release['medium-list'])
+
+                for track in medium['track-list']:
+                    recording = track['recording']
+                    albumdata['tracks'].append({
+                        'title': recording['title'],
+                        'artist': recording['artist-credit-phrase']
+                    })
+
+                result.append(albumdata)
         return result
 
     @classmethod
@@ -241,7 +247,17 @@ class Albumdata:
                 print('=' * max_width)
                 print('Albumdata sources available:')
                 for i in range(1, len(results) + 1):
-                    print('{}) {}'.format(i, results[i - 1]['source']))
+                    result = results[i - 1]
+                    source_name = result['source']
+                    extended_title = result['title']
+                    if result['cd_count'] > 1:
+                        extended_title += ' (CD {})'.format(
+                            result['cd_number'])
+
+                    print('{}) {}: {}'.format(
+                        i,
+                        source_name,
+                        extended_title))
                 print('=' * max_width)
             else:
                 print('Source {}: {}'.format(
@@ -497,7 +513,9 @@ class Albumdata:
             'title': '',
             'date': '',
             'albumartist': '',
-            'tracks': []
+            'tracks': [],
+            'cd_number': 1,
+            'cd_count': 1
         }
 
         # We do this to avoid emitting anchors in the yaml (which it
@@ -516,9 +534,12 @@ class Albumdata:
             # Check that the track count is correct
             if len(result['tracks']) != track_count:
                 print(' '.join("""\
-                    Warning: Source {} dropped for wrong track count (Got {},
-                    {} expected)""".format(
-                        result['source'], len(result['tracks']), track_count
+                    Note: Dropped CD {} of source {} for
+                    wrong track count (Got {}, {} expected)""".format(
+                        result['cd_number'],
+                        result['source'],
+                        len(result['tracks']),
+                        track_count
                     ).split()), file=sys.stderr)
                 dropped.append(result)
                 continue


Test patch:
diff --git a/tests/test_albumdata.py b/tests/test_albumdata.py
index 8867a8c..b544f26 100644
--- a/tests/test_albumdata.py
+++ b/tests/test_albumdata.py
@@ -15,6 +15,8 @@ testdata = {
         'title': 'Test album',
         'albumartist': 'Test Artist',
         'date': '2018-01',
+        'cd_count': 1,
+        'cd_number': 1,
         'tracks': [
                 {
                     'title': 'Test track',
@@ -350,7 +352,7 @@ def test_select_albumdata(capsys, monkeypatch, albumdata):
     expected.append("""\
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -375,7 +377,7 @@ a: abort
 > All sources viewed, returning to listing
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -385,7 +387,7 @@ a: abort
 
 > ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -396,7 +398,7 @@ a: abort
 > You can only choose current source when looking at a source
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -423,7 +425,7 @@ a: abort
     expected.append("""\
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -434,7 +436,7 @@ a: abort
 > Source number must be between 1 and 1
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -447,7 +449,7 @@ a: abort
     expected.append("""\
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -458,7 +460,7 @@ a: abort
 > Invalid command: tööt
 ============================================================
 Albumdata sources available:
-1) Test data
+1) Test data: Test album
 ============================================================
 0: return to listing
 1-1: select source
@@ -486,6 +488,24 @@ a: abort
         assert out == expected[test_index]
 
 
+def test_select_albumdata_multicd(capsys, monkeypatch, albumdata):
+    fake_input_generator = (a for a in ('a',))
+    def fake_input(prompt):
+        print(prompt, end='')
+        try:
+            return next(fake_input_generator)
+        except StopIteration:
+            return
+
+    testdata['cd_number'] = 3
+    testdata['cd_count'] = 3
+
+    monkeypatch.setattr('builtins.input', fake_input)
+    albumdata.Albumdata._select_albumdata([testdata])
+    out, err = capsys.readouterr()
+
+    assert "1) Test data: Test album (CD 3)" in out
+
 def test_previous_result(monkeypatch, albumdata):
     monkeypatch.setattr('os.path.isfile', lambda *x: True)
     monkeypatch.setattr('builtins.open', lambda *x: io.StringIO(yaml.safe_dump(testdata)))