Question about conditional path formating

I’ve had quite a search to getting my albums display like this:

**Nils Frahm**
Spaces (2013) (FLAC 24-44)
All Encores (2019) (FLAC 24-96)
Solo Remains (2016) (FLAC 16-96)
Solo (2015) (FLAC 24-96)
Screws Reworked (2015) (FLAC 16-44)
Screws (2012) (FLAC 16-44)
Felt (2011) (FLAC 16-44)
All Melody (2018) (FLAC 24-96)

I’m mostly very happy with it because I can quickly determine which albums are in MP3 and which are hires FLAC files. But since the majority of my files are FLAC 16-44 I’m wondering if and how it would be possible to make another condition in the path below to still have e.g. exceptions like (MP3 236) and (FLAC 24-96) added to the path but have (FLAC 16-44) omitted.

paths:
  default: 'Albums/%the{$albumartist}/$album (%if{$original_year,$original_year}) %aunique{albumartist album year, albumdisambig}%if{$albumdisambig,($albumdisambig $year) }%if{$is_flac,($format $bitdepth-$my_samplerate),($format $av_bitrate)}/%if{$multidisc,$disc-}$track - $title'

I’ve read the manual regarding ‘if condition’ but as far as I’ve determined what I like to achieve isn’t possible with it?

My inline section of the config looks like this:

# Inline plugin template
item_fields:
  multidisc: 1 if disctotal > 1 else 0
  my_samplerate: round(samplerate / 1000)
  is_flac: 1 if format == "FLAC" else 0
album_fields:  
  format: |
       formatList = []
       for item in items:
           formatList.append(item.format)
       return formatList
  av_bitrate: |
       total = 0
       for item in items:
           total += item.bitrate
       return round(total / len(items) / 1000)
  album_bitdepth:  |
       total = 0
       for item in items:
           total += item.bitdepth
       return round(total / len(items))
  album_samplerate:  |
       total = 0
       for item in items:
           total += item.samplerate
       return round(total / len(items) / 1000)

Thanks, Jan

Maybe you want another inline field like is_1644 that checks for that specific combination? Then you can use another %if like your others to omit the numbers in that case.

A terrific, so you can nest ‘if’ conditions. I couldn’t determine that from the docs could be a good addition to add that. Thank for your suggestion.

It was to be expected (lacking coding skills) that I wouldn’t be able to fix this on my own.
Please only answer if it’s not too much asked.

I’ve created a new is_1644 inline statement like this (my whole inline section is at the bottom for reference)

  is_1644: 1 if bitdepth == 16 and my_samplerate == 44 else 0

And I’ve changed this part of my path from

%if{$is_flac,($format $bitdepth-$my_samplerate),($format $av_bitrate)}

to

%if{$is_1644,,%if{$is_flac,($format $bitdepth-$my_samplerate),($format $av_bitrate)}}

When i do

beet move 'Rickie Lee Jones'

I get the following error

beet move 'Rickie Lee Jones'
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beetsplug/inline.py", line 112, in _expr_func
    return eval(code, values)
  File "inline", line 1, in <module>
NameError: name 'bitdepth' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 598, in substitute
    res = self.compiled(values, functions)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 622, in wrapper_func
    args[VARIABLE_PREFIX + varname] = values[varname]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/library.py", line 415, in __getitem__
    value = self._get(key)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/library.py", line 404, in _get
    return self._get_formatted(self.album, key)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 81, in _get_formatted
    value = model._type(key).format(model.get(key))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 443, in get
    return self[key]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 354, in __getitem__
    return getters[key](self)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beetsplug/inline.py", line 114, in _expr_func
    raise InlineError(python_code, exc)
beetsplug.inline.InlineError: error in inline path field code:
1 if bitdepth == 16 and my_samplerate == 44 else 0
NameError: name 'bitdepth' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beetsplug/inline.py", line 112, in _expr_func
    return eval(code, values)
  File "inline", line 1, in <module>
NameError: name 'bitdepth' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/bin/beet", line 11, in <module>
    load_entry_point('beets==1.4.9', 'console_scripts', 'beet')()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/__init__.py", line 1266, in main
    _raw_main(args)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/__init__.py", line 1253, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/commands.py", line 1551, in move_func
    opts.timid, opts.export)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/commands.py", line 1498, in move_items
    objs = [o for o in objs if (isalbummoved if album else isitemmoved)(o)]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/commands.py", line 1498, in <listcomp>
    objs = [o for o in objs if (isalbummoved if album else isitemmoved)(o)]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/ui/commands.py", line 1496, in <lambda>
    isitemmoved = lambda item: item.path != item.destination(basedir=dest)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/library.py", line 873, in destination
    subpath = self.evaluate_template(subpath_tmpl, True)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 602, in evaluate_template
    self._template_funcs())
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 600, in substitute
    res = self.interpret(values, functions)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 592, in interpret
    return self.expr.evaluate(Environment(values, functions))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 282, in evaluate
    out.append(part.evaluate(env))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 217, in evaluate
    arg_vals = [expr.evaluate(env) for expr in self.args]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 217, in <listcomp>
    arg_vals = [expr.evaluate(env) for expr in self.args]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 282, in evaluate
    out.append(part.evaluate(env))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/util/functemplate.py", line 184, in evaluate
    if self.ident in env.values:
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/_collections_abc.py", line 666, in __contains__
    self[key]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/library.py", line 415, in __getitem__
    value = self._get(key)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/library.py", line 404, in _get
    return self._get_formatted(self.album, key)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 81, in _get_formatted
    value = model._type(key).format(model.get(key))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 443, in get
    return self[key]
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beets/dbcore/db.py", line 354, in __getitem__
    return getters[key](self)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/beetsplug/inline.py", line 114, in _expr_func
    raise InlineError(python_code, exc)
beetsplug.inline.InlineError: error in inline path field code:
1 if bitdepth == 16 and my_samplerate == 44 else 0
NameError: name 'bitdepth' is not defined

On the internet I found that these errors occur when a name has not been defined before it is referenced. But that’s as far as I’ve come. Could well be that my syntax for the inline statement is incorrect and that my path needs adjustments.

Any suggestions are welcome.

My whole inline section fro reference

# Inline plugin template
item_fields:
  multidisc: 1 if disctotal > 1 else 0
  my_samplerate: round(samplerate / 1000)
  is_flac: 1 if format == "FLAC" else 0
album_fields:  
  format: |
       formatList = []
       for item in items:
           formatList.append(item.format)
       return formatList
  av_bitrate: |
       total = 0
       for item in items:
           total += item.bitrate
       return round(total / len(items) / 1000)
  album_bitdepth:  |
       total = 0
       for item in items:
           total += item.bitdepth
       return round(total / len(items))
  album_samplerate:  |
       total = 0
       for item in items:
           total += item.samplerate
       return round(total / len(items) / 1000)
  is_1644: 1 if bitdepth == 16 and my_samplerate == 44 else 0

You’ll need to put that on items, not albums, because $bitdepth is a track-level field. (Or else explicitly iterate over the items like you do in the definition of album_bitdepth.)

Ah yes indeed. So I’ve tried the following… I put the line

  is_1644: 1 if bitdepth == 16 and samplerate == 44.1kHz else 0

below in item_fields. I figured I needed to use samplerate instead of the album_field album_samplerate but didn’t get the syntax right. I got a EOF error.
I read in the manual that Beets outputs the samplerate with units like 44.1kHz.
I’ve tried all sorts of combinations, removed ‘kHz’, placed the output between single and double quotes but still a EOF.

So then I tried to write the inline setting in such a way (which felt more logical because it is about the album) that I could place it in album_fields

  is_1644: 1 if album_bitdepth == 16 and album_samplerate == 44 else 0

But that gives me the following error:

beetsplug.inline.InlineError: error in inline path field code:
1 if album_bitdepth == 16 and album_samplerate == 44 else 0
NameError: name 'album_bitdepth' is not defined.

I don’t understand because I thought album_bitdepth is already defined in the inline fields above the is_1644 line.

Do you have additional suggestions for what I might be overlooking?
Thanks!

They are not necessarily defined in that order, so I don’t think you can depend on album_bitdepth already being defined by the time that the code for is_1644 runs. You may need to “re-calculate” the album bit depth in that code.

After reading on the internet and experimenting I’ve not yet found a way how I can re-calculate album bitdepth and album samplerate both together in the same name definition so that I can set album level name is_1644 to 1 or 0.

Are there Python wizards (maybe someone else then @Adrian because he already helped a lot) that know how to achieve this?

Thanks, Jan

Based on what @adrian is saying above:

I don’t think you can depend on album_bitdepth already being defined by the time that the code for is_1644 runs…

This is just a wild guess that might work pulling together all your code into one single field:

is_1644: |
  br = 0
  bd = 0
  for item in items:
    br += item.bitrate
    bd += item.bitdepth
  br = round(br / len(items) / 1000))
  bd = round(bd / len(items))
  return 1 if bd == 16 and br == 44 else 0
1 Like

:bomb:TYPO ahead! :bomb:

br = round(br / len(items) / 1000)

Hi Adam,

Thanks a lot for your code. I created something like this but didn’t use the right order for the lines I realise now. I pasted it in my inline section which now looks like this:

# Inline plugin template
item_fields:
  multidisc: 1 if disctotal > 1 else 0
  my_samplerate: round(samplerate / 1000)
  is_flac: 1 if format == "FLAC" else 0
album_fields:  
  format: |
       formatList = []
       for item in items:
           formatList.append(item.format)
       return formatList
  av_bitrate: |
       total = 0
       for item in items:
           total += item.bitrate
       return round(total / len(items) / 1000)
  album_bitdepth:  |
       total = 0
       for item in items:
           total += item.bitdepth
       return round(total / len(items))
  album_samplerate:  |
       total = 0
       for item in items:
           total += item.bitdepth
       return round(total / len(items))
  is_1644: |
       br = 0
       bd = 0
       for item in items:
           br += item.bitrate
           bd += item.bitdepth
       br = round(br / len(items) / 1000)
       bd = round(bd / len(items))
       return 1 if bd == 16 and br == 44 else 0

When I run e.g.
beet move artistname on a folder with a 16/44 album the (FLAC 16/44) still gets appended.
My Path section for albums looks like this:

paths:
  default: 'Albums/%the{$albumartist}/$album (%if{$original_year,$original_year}) %aunique{albumartist album year, albumdisambig}%if{$albumdisambig,($albumdisambig $year) }%if{$is_1644,,%if{$is_flac,($format $bitdepth-$my_samplerate),($format $av_bitrate)}}/%if{$multidisc,$disc-}$track - $title'

Could there be a mistake in how I formatted this part?
%if{$is_1644,,%if{$is_flac,($format $bitdepth-$my_samplerate),($format $av_bitrate)}}

Thanks for any ideas.

I think that you should first make certain that the $is_1644 is working correctly on the album. Can you add (temporarily) the $is_1644 to the file_name so you can see its value? Do you get 1?

You can also use beet ls -f '$artist - $title - $is_1644' or similar to test out a custom field.

Thanks for your suggestions @jakabadambalazs and @adrian.

I’ve tried both methods but both show a 0 with albums that should give a 1 for is_1644.

One thing I was wondering about was that Adrians code displays if tracks conform to is_1644 while is_1644 is a inline album_field.
On all tracks (including 16/44s) that I tried it on it gives a 0.

I’ve tried to run the command with -a option
beet ls -af '$artist - $title - $is_1644' Rickie
but that didn’t work. I just got the following output for the two albums that I have from Rickie Lee Jones. One is a FLAC 16/44 and the other is a AAC 320.
$artist - $title - 0
$artist - $title - 0

Seemingly the inline code that Adam provided doesn’t yet work although it looks logical to me. Do you guys have an idea what might be going wrong? Thanks for your help!

You could try debugging your code by breaking it down into pieces. That is, keep retrying that beet ls -af command while modifying your code repeatedly to see what the effect is. For example, try changing the return line to return bd or similar to find out what the value of the bd variable is for several albums. Then try return br. That might reveal if those calculations are going right.

I can see you’re a teacher @adrian. I learn a lot from how you do suggestions!
I’ve broken it all into pieces and discovered that firstly I used the wrong variable in the code.

it should have been bitdepth and samplerate, not bitrate, that gives a different value. I found this out through your suggestion to break it up into pieces and see what output that gave.
So I adapted @jakabadambalazs code to this:

is_1644: |
       bd = 0
       sr = 0
       for item in items:
           bd += item.bitdepth
           sr += item.samplerate
       bd = round(bd / len(items))
       sr = round(sr / len(items) / 1000)
       return 1 if bd == 16 and sr == 44 else 0

The only thing that I didn’t realize that an MP3 230 album can just as well have a 16 bitdepth and 44 samplerate. So I also needed to check for av_bitrate > 320 as well. That would distinguish a AAC and MP3 from FLACs.
So the is_1644 ended up looking like this

  is_1644: |
       bd = 0
       sr = 0
       br = 0
       for item in items:
           bd += item.bitdepth
           sr += item.samplerate
           br += item.bitrate
       bd = round(bd / len(items))
       sr = round(sr / len(items) / 1000)
       br = round(br / len(items) / 1000)
       return 1 if bd == 16 and sr == 44 and br > 320 else 0

Thanks a lot for your help @adrian and @jakabadambalazs. Really appreciated.

1 Like

Hey. Just wanted to comment my solution to this. Probably not as elegant but its doing the job (so far lol) by using the replace function.

paths: default: '... $format $bitdepth-$samplerate ...'

replace:
    ' 0-44kHz': ~
    ' 16-44kHz': ~
    '16-96kHz': 16-96
    '24-96kHz': 24-96
    '24-192kHz': 24-192

I figure you could probably use the same thing and do ’ FLAC 16-44kHz’: ~ to omit regular flac files. I keep folder names tidy with no extra spaces by including the space in the replace function. This solution doesn’t work for MP3 files with other bitdepths/samplerates though ofc.

When seeing @oldsweatyman solution I thought I’ll post my final solution for conditional path formatting. It might be useful for others.

Most of my albums are FLAC 16-44
Since this would polute my list visually if it would be added to most of my albums I decided to come up with a solution that will only add
(MP3 bitrate)
or
(FLAC bitdepth-samplerate)
for albums that are MP3 or hires FLACs.

This could give a result like this:

Four Tet
2020 - Parallel (FLAC 24-44)
2003 - Rounds
2001 - Pause (MP3 278)

Since my inline code calculates the average album values this leads in very rare cases (where a FLAC album has mixed bit depth) to a result with a non existing bit depth, e.g.:

Talking Heads
1985 - Stop Making Sense (FLAC 23-48)

This is the inline section of my config file.

# Inline plugin template
item_fields:
  multidisc: 1 if disctotal > 1 else 0
  my_samplerate: round(samplerate / 1000)
  is_flac: 1 if format == "FLAC" else 0
album_fields:  
  format: |
       formatList = []
       for item in items:
           formatList.append(item.format)
       return formatList
  av_bitrate: |
       total = 0
       for item in items:
           total += item.bitrate
       return round(total / len(items) / 1000)
  album_bitdepth:  |
       total = 0
       for item in items:
           total += item.bitdepth
       return round(total / len(items))
  album_samplerate:  |
       total = 0
       for item in items:
           total += item.samplerate
       return round(total / len(items) / 1000)
  is_1644: |
       bd = 0
       sr = 0
       br = 0
       for item in items:
           bd += item.bitdepth
           sr += item.samplerate
           br += item.bitrate
       bd = round(bd / len(items))
       sr = round(sr / len(items) / 1000)
       br = round(br / len(items) / 1000)
       return 1 if bd == 16 and sr == 44 and br > 320 else 0

And my default path looks like this:

paths:
  default: 'Albums/%the{$albumartist}/%if{$original_year,$original_year} - $album %aunique{albumartist album year, albumdisambig}%if{$albumdisambig,($albumdisambig $year) }%if{$is_1644,,%if{$is_flac,($format $album_bitdepth-$album_samplerate),($format $av_bitrate)}}/%if{$multidisc,$disc-}$track - $title'

Hope this is useful!

2 Likes