Some Questions Related to the Hook Plugin

I have a few questions in regards to using the Hook plugin.

  1. Is there an event/parameter combination that provides the destination path of an item or album after it has been imported? I thought perhaps album_imported or after_write might do it, but their respective album.path and item.path are the source paths rather than the destination paths.

  2. Is there a way to present the paths returned by event parameters as strings rather than bytes objects? For example, after configuring a hook to return the path of an imported item, this is the result:

    /usr/bin/ls: cannot access “b’/tmp/music/new/Al Di Meola - Elegant Gypsy’”: No such file or directory

The relevant configuration entry (this is just a simple example to illustrate what I’m referring to; I realize it is of little use):

hook:
hooks:
- event: album_imported
command: /usr/bin/ls -l “{album.path}”

FWIW, I am using beets 1.4.7 with Python 3.6.5 on Arch Linux. (The directory “/tmp/music/new/Al Di Meola - Elegant Gypsy” does exist at the time of the import, by the way.)

  1. Do multiple hooks from the same event run in parallel or in series? In other words, if I were to configure two separate hooks for the import event, would the first hook in my configuration complete prior to the second hook beginning?

  2. Now on to the specific issue I’m trying to solve. I use the LastGenre plugin to write GENRE tags to my imported music. I configure the plugin to include up to four values (“count: 4”). This adds a single GENRE tag with possibly multiple values, separated by a semicolon (I use “separator: '; '”). This is fine, but it seems some software doesn’t recognize multiple values within a single tag.

I use Quod Libet as the music player on my desktop and laptop. It treats multiple values in a single GENRE tag as one long messy genre. In order to list albums and songs within each genre, it needs multiple GENRE tags with a single value in each.

To work around this I have written a shell script that takes a tag name and music file path as arguments, checks if the given tag has multiple values separated by a specific delimiter, splits those values, removes the tag in question, and adds multiple tags back to the file, one per value. This works great. However, I would like to automate this process during album imports within beets. Hence my interest in the Hook plugin. I can add a hook that calls my shell script after an import event, and run it against files in my library older than a certain age. This does the trick, but I’d prefer something more precise such as an after_write event with the destination of the written file as a string.

If a better way to accomplish what I’m trying to do exists, please let me know. I’d like to avoid writing a plugin for this, but if that’s what it takes, I can look in to it. Alternatively, would it be worthwhile to create an issue on GitHub to request extending the LastGenre plugin to offer a configuration item to create multiple GENRE tags for the case where the plugin is configured with “count” greater than 1?

Even if there is a better way than my current “Hook plugin and shell script” approach, I’d still be interested in answers to my questions 1) through 3) for future reference.

Thanks for your time, and thanks for beets. It has been an incredible addition to my music cataloguing workflow.

Hmm; I’m surprised that’s the case—I would have expected this to see the new paths.

This looks like a bug—we should somehow insert paths directly instead of getting their literal representation. I could have sworn this has come up before, but I can’t seem to find the GitHub issue I was thinking of. Would you mind filing a new one?

It’s pretty complicated and it depends on the event, but with the various import stages, handlers for the same event should run serially. (But different events may happen in parallel.)

Supporting multiple metadata tags is a longstanding architectural issue. Here’s a good place to start to read more about the background:

Ignore this. I took another look at the config I was using for my testing, and I had the “copy” option set to “no” for the import command. After setting “copy: yes”, running “beet rm”, and re-importing my test files, the above parameters were indeed returning the correct destination paths. With “copy: no”, the source and destination were the same path. Sorry about the false report.

Issue #2967 has been created. As noted at the end of my bug report, if I run beets using Python 2 the values returned for paths are usable by the shell. Perhaps I’ll start running beets under Python 2, unless there are compelling reasons to avoid this.

Thanks, I’ll get reading. In the meantime, the current work around of using my shell script to make the necessary tag changes is acceptable.

Great; thanks for filing that issue! Yes, using Python 2 for now should be fine.