Beets path handling for delimited fields

I know there have been a couple of feature requests regarding delimited fields for genres and so on, but I’m not sure to what extent we currently have support for those. The %first template function for paths lets us split out a specific entry from a delimited field, but what I’m really wanting to do is expand the field so that I can deal with the individual entries all at once–specifically with the Smart Playlists plugin.

For my use case, I’m a fan of mixtapes, so I have a flexible attribute called “mix” which lists all the mixtapes that a single track is a part of, semicolon-delimited. I would like to define a Smart Playlist which looks at that field and builds a .m3u file for each unique mix. Something like this would work if there was only one entry in that field:

playlists:
 - name: 'mixes/$mix.m3u'
   query: '^mix::^$'

So if I have mixes A and B, tracks included in one mix or the other would be included in A.m3u or B.m3u respectively. But say I have the track “Alleluia” by Odawas in both mix A and B. The content of its “mix” field would look like “A; B”… and I end up with a file called “A; B.m3u” listing that track, when what I need is for that track to be listed in A.m3u and B.m3u both. I considered defining a separate playlist for each delimited entry, a bit like this:

playlists:
 - name: 'mixes/%first{$mix,1,0}.m3u'
   query: '^mix::^$'
 - name: 'mixes/%first{$mix,1,1}.m3u'
   query: mix:";"

But it looks like that just results in the contents of the first playlist file getting overwritten with the content of the second. Is there any way to get beets to do what I’m wanting that perhaps I haven’t thought of?

This is an interesting problem—it does seem like somewhere we could benefit from better support for “tag-like” soups of multiple strings. It would also be interesting to think about the ideal way to solve this—perhaps with separate attributes for each mix a song is part of?—but it also seems like this should be solvable.

Your design here is very creative and I agree it should work. The problem, currently, is that the plugin isn’t expecting multiple playlist declarations to create overlapping sets of playlists. So the second entry in that YAML list overwrites the B.m3u created by the first entry.

I think the right way to solve this is just to let all declarations build up the same set of shared playlists. I can’t see how this would break anyone’s existing configuration. Here’s a pull request: https://github.com/beetbox/beets/pull/2468

Any chance you could try that out?

The pull request works great! (And doesn’t break the moment I touch it, which is remarkable in itself.) Man, how do I get this kind of turnaround time in the rest of my life…?

Anyway yes: allowing smart playlists to overlap is perfect for this use case, now you mention it–and would allow more flexible use of the plugin in general. It looks like it’s de-duplicating as well, correct–so a track won’t be listed twice in a playlist? Could handling it this way (cumulatively instead of sequentially) cause it to choke on the number of tracks or something similar?

For the larger question of multi-valued/taglike fields, yeah, there’s a lot of possible ways to handle it. I originally was thinking of this in the context of genre fields, and I think the topic’s been discussed before to an extent (here and here).

When you say “separate attributes for each mix”, you mean a flexible attribute like “A=true” or “B=true” for each track on mix A or B respectively? That’s do-able, but it would restrict lookup compared to using a delimited/list field: rather than being able to reference all the mixtapes via the $mix tag (as in the smart playlist config), you would need to either remember the mix name or the track.

Yep, that’s true. We avoid duplicated, but we do pay the performance cost of checking to avoid duplicates.

Right, that’s more or less what I was proposing. And you’re also right about the drawback: it also makes it harder to write a query like mix:foo to match mixes named both foo1 and foo2. But maybe there’s some middle ground that would make sense here… it would require, however, slightly more creativity than I have at the moment. :slight_smile:

There are a number of attributes that could be better with explicit support for multiple entries.

Genres are the obvious example (and surely I’m not the first person to think of genres as a directed acyclic graph, and all that entails).

Artists also form a DAG, and of course it’s very desirable to be able to assign multiple artists to a track too. I think the correct approach for artists is to be able to assign as many artists as you please to a work, but to be able to set roles, e.g. primary, guest, composer, remixer.

Key, tempo, time signatures all can vary within a single track too.

(Getting well out of the scope of this thread, we can actually treat works as a DAG…)

I’m not sure how much of the above is limited by the likes of ID3. Certainly some of it would be limited by compatibility with existing schemas.

In fact, work on that exact feature—multi-valued tags—has recently been revived!
https://github.com/beetbox/beets/pull/2503

2 Likes