Querying For Specifc Genre Tags When Files Have Multiple Genres

I spent a bit of time trying to solve this issue and I searched here for an answer, but before I spend too much time I’m hoping someone here can help out.

The files in my library may be tagged with multiple genres, separated by '; '. I’d like to be able to query my library for specific (non-substring) genre values. Thus far I haven’t been able to put together the logic to do so.

Some examples may help. Let’s assume there are four songs in my library that are tagged as follows:

Song #1 genres: Southern Rock; Rock; Hard Rock; Classic Rock
Song #2 genres: Rock; Progressive Rock; Classic Rock; Experimental Rock
Song #3 genres: Post-Punk; Folk Punk; Rock; Indie Rock
Song #4 genres: Indie Rock; Indie Pop

I want to query the library for songs with the genre “Rock” without returning other songs that do not have the specific genre “Rock”. In other words, the query should return Songs 1, 2, and 3 but NOT return Song 4. Song 4 has a genre of “Indie Rock” but not “Rock”, so I want it excluded from this example query.

Before I submitted this topic I wanted to take another stab at it. I was able to get it to work with the following example:

beet ls genre::'[^\w]\s\bRock\b', genre::'^Rock\b'

But there should be a regex that gets it done in a single expression. I thought a negative lookahead should do the trick but I wasn’t getting it to work. (I realized after I had started to compose topic that this is a general regex question, and not a beets specific question.)

@mike You can use groups in parentheses with | (or):

beet ls genre::'(^|[^\w]\s)Rock([;,]|$)' -f '$genre'

The three main parts here are:

  1. (^|[^\w]\s) : either the start of the string, or a non-word character followed by a whitespace character
  2. Rock : the genre, of course
  3. ([;,]|$) : either a separator character (; or ,), or the end of the string

You don’t need \b here because there will always be a word boundary between a whitespace character and the first word character of the genre name.

I added the third part because you might have some characters after the word Rock; examples from my collection include Rock/Pop, Rock/Unknown, and Rock & Roll, all of which match Rock\b because the / and space characters count as word boundaries.

@ctrueden Thank you for the regex and the description of each piece. This seems to be working as I expect.

I had already run in to scenarios that led me to add (;|$) to the end of my first regex, but I forgot to come back here to update my original post.

Thank you again for taking the time to respond with a complete regex that does what I was looking for.

1 Like