Repository: beetbox/beets

Issue:
discogs: (Optionally) incorporate index tracks in track titles
### Use case
I use beets to tag and organize my classical music collection. It's common for classical releases to include recordings of multiple works, each divided into multiple parts, each of which may in turn be spread across several tracks. As a running example, [this album](https://www.discogs.com/Handel-Sutherland-Kirkby-Kwella-Nelson-Watkinson-Bowman-Rolfe-Johnson-Elliott-Partridge-Thomas-The-A/release/2026070) includes four multi-part oratorios, and each aria, chorus, recitative, etc. within each part gets its own track (modulo some coalescing that isn't important here). Note how Discogs uses "index tracks" to separate the track list hierarchically by work and part.

When I turn on the Discogs plugin and tag this album, beets ignores Discogs' index tracks (except for deducing disk names), so I get track names like

```
No.1: Sinfony
No.2: Recitative- Comfort Ye My People / No.3: Song- Every Valley Shall Be Exalted
No.4: Chorus- And The Glory Of The Lord
```

Because this style is confusingly vague ("Wait, which oratorio am I listening to?"), the standard practice for classical music is to incorporate the work name and any intra-work divisions into each track title, thus:

```
Messiah, Part I: No.1: Sinfony
Messiah, Part I: No.2: Recitative- Comfort Ye My People / No.3: Song- Every Valley Shall Be Exalted
Messiah, Part I: No.4: Chorus- And The Glory Of The Lord
```

This happens automatically when I tag from MusicBrainz, and I'd like to be able to make it happen when using Discogs.

### Solution
[Here](https://github.com/beetbox/beets/blob/20630d194717cca71886e56e498c4b52c8073ae0/beetsplug/discogs.py#L400)'s where the current treatment of index tracks is defined in `discogs.py`. The alternative behavior I have in mind is something like this: when scanning a tracklist from Discogs, beets keeps a stack of "counters", each containing the title of a subdivision—in the Handel album, there'd be one for the current work title (`"Messiah"`) and one for the current part (`"Part I"`). When a block of `k` successive index tracks is encountered, the `k` fastest-changing counters get "bumped". The title of each non-index track gets prefaced with the current values of the counters (plus some delimiters).

This behavior could be switched on with an option in the `discogs` section, maybe `incorporate_index_tracks`.

I'd be happy to try to work up a pull request implementing this feature if it sounds reasonable to the maintainers.

### Alternatives
I don't know of any way to configure this behavior with existing features.



Discussion:
Sounds great! Thank you for the detailed background on this; I feel I actually understand the issue now. 😊

If you're interested, please do go right ahead and try an implementation. May I suggest a simpler option name—something like `index_title` or `index_track_title` or even just `index_tracks`?

Resolving patch:
diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py
--- a/beetsplug/discogs.py
+++ b/beetsplug/discogs.py
@@ -57,7 +57,8 @@ def __init__(self):
             'tokenfile': 'discogs_token.json',
             'source_weight': 0.5,
             'user_token': '',
-            'separator': u', '
+            'separator': u', ',
+            'index_tracks': False,
         })
         self.config['apikey'].redact = True
         self.config['apisecret'].redact = True
@@ -397,14 +398,28 @@ def get_tracks(self, tracklist):
         tracks = []
         index_tracks = {}
         index = 0
+        # Distinct works and intra-work divisions, as defined by index tracks.
+        divisions, next_divisions = [], []
         for track in clean_tracklist:
             # Only real tracks have `position`. Otherwise, it's an index track.
             if track['position']:
                 index += 1
-                track_info = self.get_track_info(track, index)
+                if next_divisions:
+                    # End of a block of index tracks: update the current
+                    # divisions.
+                    divisions += next_divisions
+                    del next_divisions[:]
+                track_info = self.get_track_info(track, index, divisions)
                 track_info.track_alt = track['position']
                 tracks.append(track_info)
             else:
+                next_divisions.append(track['title'])
+                # We expect new levels of division at the beginning of the
+                # tracklist (and possibly elsewhere).
+                try:
+                    divisions.pop()
+                except IndexError:
+                    pass
                 index_tracks[index + 1] = track['title']
 
         # Fix up medium and medium_index for each track. Discogs position is
@@ -539,10 +554,13 @@ def add_merged_subtracks(tracklist, subtracks):
 
         return tracklist
 
-    def get_track_info(self, track, index):
+    def get_track_info(self, track, index, divisions):
         """Returns a TrackInfo object for a discogs track.
         """
         title = track['title']
+        if self.config['index_tracks']:
+            prefix = ', '.join(divisions)
+            title = ': '.join([prefix, title])
         track_id = None
         medium, medium_index, _ = self.get_track_index(track['position'])
         artist, artist_id = MetadataSourcePlugin.get_artist(


Test patch: