sampsyo/beets — pull request #527 by geigerzaehler (2014-02-04 19:18:29+00:00)
Title: Tags with multiple values

These are first steps towards multivalued tags (see #505). As of now, the branch includes

* Refactoring of tests

* Refactoring of the `StorageStyle` implementation

* Experimental support for genre lists in MP3 files.

It does not break the current Mediafile API. It also doesn't interface the additions with other parts of beets*, e.g. the library.

## Rationale

Lists are of prime importance for genres (see #119, #437). In addition, there are several fields not currently in the *beets* core that would benefit from lists. Most specifically *MusicBrainz* relations like "performer", "composer", etc.

I am pretty sure there are many more use cases out there and **I would be glad to hear about them** to inform the implementation.

## Storing Lists in Tags

I am not yet sure how to implement this and there are many points that factor into the decision. Comments are highly appreciated.

- *Does a format support lists natively?* I surveyed this for *ID3* and *Ogg* below. But I don't know anything about the other formats. Input would be appreciated

- *How does *mutagen* treat lists?* I think *mutagen* generally treats tags as lists. It is however not clear if correct handling is supported in all formats, or whether the handling is idempotent.

- *How does other software treat lists?* I am not aware of any player that show lists of some metadata. For lists in ID3 text frames (see below) most players display them as space seperated strings and destroy this information when writing tags.

As an example, I implemented genre lists for MP3 files. There is a new `genres` property in the `MediaFile` API. It allows for getting and setting lists of genres. Tests can be found in the `MP3Test` class. At the moment we use `0x00` seperated strings to store lists (see below).

### List of tags for ID3

ID3 has two different ways of storing lists. First, text frames (frames whose identifier starts with "T", excluding "TXXX", see [Frames,4.2](http://id3.org/id3v2.4.0-frames)) may contain lists of strings seperated by the terminal character of the encoding (`0x00` for UTF8). Secondly, there are several frames that can appear multiple times, but have to be unique up to an additional parameter (e.g. description for "TXXX"). This restricts storing lists severly.

I know how *mutagen* handles this so writing code for this should not be an issue.

### List of tags for VorbisComment

Storing lists is well defined for [Vorbis comments](http://www.xiph.org/vorbis/doc/v-comment.html). In fact storing single values is just a special case of storing a list with one element. Hence, we also should expect no issues here.

### Other Formats

I have not yet looked into other tagging schemes. Any input would be appreciated.

## Implementation details

### Test refactoring

The tests in `test_mediafile_basic.py` were refactored to be more manageable. Each file type has its own test case with some general tests mixed in from `ReadWriteTestBase` and possibly `PartialTestMixin`. I also added more files with empty tags to test against.

### Mediafile refactoring

Previously, all the code responsible for interacting with *mutagen* was contained in the `MediaField` class. This was moved into the `StorageStyle` class. Then, instead of checking fields like `style.id3_desc`, I used subclasses to implement the file type specific behaviour. I am not sure whether this refactoring did change the behaviour. If so, this is not covered by the test cases. Help in spotting bugs would be appreciated.