-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom tag mappings when reading and writing metadata #2621
Comments
Thanks for the suggestion! This would certainly be an interesting addition for advanced users. Currently, some users even have trouble understanding that there is a level of indirection between the "beets names" for fields and how they appear in files' tags. Making that indirection customizable could help clarify what's going on internally in beets. Here are a few concerns I foresee in implementing this:
FWIW, this is also relevant to #350, which is a specific case of wanting different mappings from built-in fields to Vorbis/etc. tag names. |
I was thinking of making the change internal to the MediaFile class kind of like:
My knowledge of beets (and python in general) is limited but I think that would be a way of implementing this that is contained to the class itself and not requiring large architectural changes. I agree dependency injection or other major changes would be needed for dealing with flex attrs and plugins but the above might work just for the standard fields. Regarding fields like date, year and ReplayGain, they are more complex but I think it's still possible to use a custom name for them while maintaining the additional parameters supplied to the storage style such as |
Yes! I do agree that changes shouldn't contaminate too much of beets itself, and shouldn't affect the rest of beets—but I'd still like to see them end up being provided to the constructor (or something like that) as opposed to modifying the base class in place. Anyway, if your prototype change is pushed to GitHub somewhere, I'd be happy to take a look! |
If I understand what you're suggesting, there would be a new class, say I've pushed my prototype here: master...elfalem:2621-custom-tag-map |
I was thinking of an even simpler distinction: just the difference between modifying the class with a preferred mapping and modifying the instances. Fore example, it would be nice if you had to say: mf = MediaFile('/path', { 'artist': 'my_artist' }) every time you construct a MediaFile. That's in contrast to modifying the MediaFile class itself, which (silently) affects the behavior of all MediaFile instances. (The latter is how the current tag-adding functionality works.) |
I think I'm missing why we want to modify specific instances as opposed to all. What would be the cases where MediaFile instances will be constructed with different mappings? Do we want to limit tag visibility to specific plugins? I see how it can make testing easier but wouldn't it be cumbersome in normal use to specify the same mapping everywhere? |
The problem isn't changing all the instances per se, but the effect on code maintainability. Having plugins modify the class structure has been a big headache for testing and debugging in the past. For example, it makes it really easy to make a change in one test and forget to clean it up—which affects all the other tests, potentially making them fail in mysterious ways. At the core, the problem is that the extra fields are implicit rather than explicit—so I'm just advocating that we err on the side of explicit. Passing the configuration to the constructor is one way to make things explicit. |
usually, the "global state" is "evil" |
I'm looking into passing the configuration in the constructor and it looks it's going to require changing all the fields in the MediaFile class from class attributes to instance attribute. Is that correct? If so, I think it's going to require larger changes in various places where the fields are accessed/enumerated. |
I've pushed my latest changes where I changed the lyrics field to an instance attribute. The issue now is when writing tags to a file, the lyrics field is totally ignored. |
Hmm, yes, Python descriptors only work when they're on the class (if I understand correctly). To avoid global state while still accomplishing this will require some creativity: for example, the descriptors could look up an indirection table that is itself stored on the instance. |
Thanks for your guidance! Looks like all problems can be solved by another level of indirection :). I think I have an implementation that works with all the different tags (master...elfalem:2621-custom-tag-map). Basically, I added a new The new configuration will look like:
I noticed that running tests locally might fail based on the contents of the user's configuration file. Is there a way in Confuse to only fetch configurations from the default layer? I've modified a couple of tests to work with the changes. I still need to modify the rest, and add new ones as well. |
Cool! I don't 100% understand how the new MediaFile architecture works, so I may need a more detailed introduction sometime… but for now, regarding the tests, you can get a "clean" configuration environment by using the infrastructure in |
I've added a couple of tests for the custom mapping making use of the With regards to the MediaFile architecture, in the present system, MediaField takes a tuple of StorageStyles when the field descriptors are declared in MediaFile class such as:
With my changes, there is a new
Inside MediaField class, if |
I see! Thanks for clarifying. I have two high-level suggestions for the new architecture:
Finally, one technique that might help reduce redundancy would be to drop the descriptor style for defining |
Thanks for the suggestions.
|
Also, is it possible to get a default value instead of an exception when obtaining a view of a configuration? For example, with |
There are a few common strategies:
|
Thanks for all the options. I'm learning a lot about beets. I thought of another option. What about having an empty |
Sure! Let’s start there. |
Beets currently has a predefined mapping between fields in the database and the tags they're written to for the different file formats. In some cases a field is read from or written to multiple tags (e.g.
tracktotal
is written toTRACKTOTAL
,TRACKC
, andTOTALTRACKS
Vorbis comment fields which I understand is to be compatible with a variety of music software.Many users would like to have more control over which fields are written to files and what tag names are used when doing so. This can be achieved by introducing a new configuration where the user can specify their desired mapping.
I think the biggest concern if this is to be implemented is the difference between the various audio file formats. While Vorbis comments allow free form tags, ASF, MP3 and MP4 storage styles have a more strict structure. My recommendation would be to support this custom mapping only for Vorbis comments and follow the default mapping for the other storage styles. While this will introduce a disparity between the different formats, I don't think the more restrictive formats should hold beets back from supporting a very useful customization.
While the plan for this feature request is to customize the mappings for the standard fields beets knows about, it can serve as the foundation for allowing writing (and reading) flexible attributes to files. That would make it easier for implementing issues such as #122.
I have done a small prototype where I modified the storage styles used by the MediaField descriptor assigned to the lyrics field mediafile.py based on a configuration. It seems to work as far as I can tell. I was able to write the lyrics field to a custom tag of my choosing (one, multiple or zero tags) for a FLAC file.
The text was updated successfully, but these errors were encountered: