Get initials only of artists

Hi!
I’m still working on my ideal beets setup. This involves shortening paths as windows has limits on the length of paths it can handle. One easy way to do it would be to use only the initials of artists, like Vivaldi, A. instead of Vivaldi, Antonio . If there is nothing specific, would there be a way to hack it, something like split strings with commas or other separators, then replace every second block with its initials. Is there currently a way of doing this with beets?
Thanks,
Dorian

Hello! This is probably a good case for using the inline plugin. It lets you write little Python snippets, which would be perfect for implementing custom string-manipulation logic like this.

Thanks! How exactly could I do it? How do the python snippets work? It would be something like

tmp = re.split('(\W+)',albumartist_sort) # cut artist name into pieces
a_artist_initials = ''
fname = True # marker for family name
for i in tmp: 
    if fname: # family name, kept as-is
        a_artist_initials += ' ' + i
    elif i == ',' or i == ';' or i == '&': # we want to keep the separators
        a_artist_initials += i
        fname=not fname
    elif i=='II': # for numbers in names as in Joseph Strauss II
        a_artist_initials += ' ' + i
    else: # First name
        a_artist_initials += ' ' + i[0] + '.'

How do I transform this into a snippet? Do I need to import re, how do I do this? Then I need way to deal with orchestras/choirs that don’t have family names or similar stuff.

I think the best way to sort this out would be with trial and error! Can you give it a shot and see what kinds of problems you run into?

No, I mean, how do snippets behave? How do you transform python code into a python snippet? How do you tell him what the final output is?

Sorry; I should have just said “Python code.” There’s not really any such thing as a “snippet.”

The example in the docs should be able to clarify how the code works (you just return the result):
http://docs.beets.io/en/stable/plugins/inline.html#block-definitions

Another (related) question: in a python code for inline, what is the equivalent of %ifdef ?

Beets itself does not provide one. But the values are just local variables—you may be able to pull some kind Python hack to test whether a local variable is defined, such as catching a NameError exception.

inline allows to set new variables to use for path, but can it also set the path itself? i. e. is it possible to set the entire path in inline and then just call in in path?
I’m thinking of something like

return('Non-Classique/'+albumartist_sort+'/'+album+' ('+albumdisambig')/'+disc+'-'+track+' '+title)

which doesn’t work right now, the / are replaced with underscores (and I don’t want it to stop replacing the / as it appears in some album titles). Is there a way to circumvent this?

No, that’s not currently possible.

Ok, thanks for all the clarifications. I now have the following problem: I tried

item_fields:
    pw: |
        try:
            pw=parentwork
            return pw
        except NameError:
            return 'No_Work'
path: 
    default: $pw/$albumartist_sort/$album/$disc-$track $title

I don’t get what I expect (i. e. everything that has no parentwork tag goes into a No_Work folder). Instead, all those without parentwork go in the folder corresponding to one of the works. Do you have an idea why?

I think you’re probably running into this:

Which I recently fixed on master:

Ah thanks! I’m still using an older version because of https://github.com/beetbox/beets/issues/3308 and the discussion we had on https://github.com/beetbox/beets/pull/3279 . I will update and check again.

It solved it, thanks!

Now I’m getting repeatedly errors like this:

beetsplug.inline.InlineError: <exception str() failed>

The line triggering them is:

item_fields:
    type: |
        nc_artist = []
        nc_albumartist = []
        nc_album = []
        nc_artist_sort = []
        nc_albumartist_sort= []
        if artist in nc_artist or albumartist in nc_albumartist or album in nc_album or artist_sort in nc_artist_sort or albumartist_sort in nc_albumartist_sort:
            return 'non-classical'
        medieval=['Coro de monjes de la Abadía de Montserrat, Choralschola des Klosters Maria Einsiedeln, Choralschola der Benediktinerabtei Münsterschwarzach, Chœur des moines de l’Abbaye Notre-Dame de Fontgombault, The Benedictine Monks of Santo Domingo de Silos, Cappella Musicale del duomo di Milano','Hildegard von Bingen; Ensemble Belcanto, Dietburg Spohr','Hildegard von Bingen; Gothic Voices, Christopher Page, Emma Kirkby']
        if artist in non_classical or albumartist in non_classical or album in non_classical:
            return 'medieval'
        return 'classical'
path: 
    type::'non-classical': Non-Classique/%ifdef{$album,$albumartist_sort/$album/$tr,$artist_sort/$tr}
    type::'medieval': Medieval/%ifdef{$album,$albumartist_sort/$album/$tr,$artist_sort/$tr}
    pw::'No_Work': No_Work/$albumartist_sort/$album/$tr
    work_prefix::'None':  $pc/$pw/$albumartist_sort/$album/$tr
    default: $pc/$work_prefix/$pw/$albumartist_sort/$album/$tr

($pw, $pc and $work_prefix work fine)
Do you have an idea why it behaves this way?

It would be really helpful to see what’s going wrong if you were to minimize this definition. I usually do that by deleting pieces until the problem goes away, then brining back only the part I just deleted and trying again.

Hi! The problem seems to be

medieval=['Coro de monjes de la Abadía de Montserrat, Choralschola des Klosters Maria Einsiedeln, Choralschola der Benediktinerabtei Münsterschwarzach, Chœur des moines de l’Abbaye Notre-Dame de Fontgombault, The Benedictine Monks of Santo Domingo de Silos, Cappella Musicale del duomo di Milano','Hildegard von Bingen; Ensemble Belcanto, Dietburg Spohr','Hildegard von Bingen; Gothic Voices, Christopher Page, Emma Kirkby']
if albumartist in medieval:
    return 'medieval'

I get lots of <string>:331: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
which mean that I have trouble with my encoding. It seems like some accents in the medieval list cannot be properly translated to Unicode. It seems related to http://beets.io/blog/paths.html .
If I convert everything to Unicode with

 medieval=[u'Coro de monjes de la Abadía de Montserrat, Choralschola des Klosters Maria Einsiedeln, Choralschola der Benediktinerabtei Münsterschwarzach, Chœur des moines de l’Abbaye Notre-Dame de Fontgombault, The Benedictine Monks of Santo Domingo de Silos, Cappella Musicale del duomo di Milano',u'Hildegard von Bingen; Ensemble Belcanto, Dietburg Spohr',u'Hildegard von Bingen; Gothic Voices, Christopher Page, Emma Kirkby']
if albumartist in medieval:
    return 'medieval'

then it works fine.

Hi!
I’m getting another interesting error:

beetsplug.inline.InlineError: error in inline path field code:
if singleton:
    tr=title
else:
    tr=str(disc)+'-' + str(track) + ' ' + title
return tr

NameError: global name 'singleton' is not defined

It seems like the singleton tag can’t be used as a Python variable.
I had fun getting the initials of artist lists where the artists are separated by commas only and it only includes people (not orchestras, choirs or similar stuff) , here exemplified for theparent_composer_sort tag that fulfills the above conditions:

        try: 
            parent_composer_sort.split(',')
            pc=[]
            fam=True
            for p in parent_composer_sort.split(','):
                if p=='Sir' or p=='Dame':
                    continue
                if fam:
                    pc.append(p.strip())
                else: 
                    a=p.split()
                    b=[]
                    for x in a:
                        if previous.strip()=='Strauss' or x=='van' or x=='von' or x=='de':
                            b.append(x)
                        else: 
                            b.append(x.strip()[0])
                    pc.append(' '.join(b))
                previous=p
                fam=not fam
            return(', '.join(pc))
        except NameError:
            return 'No_Parent_Composer'

more generalized splitting shouldn’t be a problem, but orchestras will be, as this piece of code supposes that every text block is a family name, then a comma, then a list of first names (that are then replaced by the corresponding initals) then the next family name. Groups mess this system up as 1. they don’t really have family and first names, so putting initals doesn’t make sense and 2. they are not always split in two parts separated by a comma, so it messes up all following names. Apart from having a list of all orchestras somewhere in a text file or making MB queries for each artist, I don’t see how to circumvent this problem.
I make an exception for the Strauss family, because I want to be able to discriminate between Josef, Johann I and Johann II.

Another interesting bit of info: for flexible attributes like parentwork if the tag is not set python raises a NameError but for core attributes like album or disc they are set to the corresponding zero (empty string or 0 as an int).