beets icon indicating copy to clipboard operation
beets copied to clipboard

Multiple plugin paths is not working

Open wcage03 opened this issue 5 months ago β€’ 2 comments

Problem

Documentation states that multiple plugin paths can be defined (https://beets.readthedocs.io/en/stable/reference/config.html#plugins - plugin path subheader). The documentation states the format should be:

pluginpath:
    - /path/one
    - /path/two

Use of the dashes causes a yaml error. This may be an error in the document. Removing the dashes allows the format to be accepted, however, the two paths do not seem to be used. I created a separate directory for a custom plugin that I developed for my own use. I want to keep it separate from the main body of beets.

Using the pluginpath configuration does not seem to be recognized by beets at all. I tested:

  • having the first path point to the default plugin directory (.../beetsplug) and having a second pluginpath pointing to my custom plugin directory
  • switching the order of the two directories so that the custom directory was first
  • putting in a non-existent directory as the sole pluginpath

In all cases delivered plugins were found (e.g. the delivered beetsplug directory was referenced) and the custom plugin was not. It appears that other than confirming that the yaml is formatted, the pluginpath is not referenced at all.

Setup

  • OS: MacOS Sequoia 15.5
  • Python version: 3.12.2
  • beets version: 2.3.1
  • Turning off plugins made problem go away (yes/no): no

My configuration (output of beet config) is:

beet config
directory: /Volumes/share/music
# --------------- Main ---------------

library: /Volumes/share/beets/library1.db

import:
    copy: no
    move: yes
    write: yes
    log: /Volumes/share/beets/import.log
    singletons: no
    asciify_paths: yes
    bell: yes
    incremental: no
    incremental_skip_later: yes
    resume: yes
paths:
    default: Albums/$albumartist/$album%aunique{}/$disc-$track $artist - $title
    singleton: Non-Album/$artist ($year) - $title
    comp: Compilations/$album%aunique{}/$disc-$track $artist - $title
    albumtype_soundtrack: Soundtracks/$album/$disc-$track $artist - $title

    pluginpath: /Users/myname/ownCloud/GitHub/beets/src/beets/beetsplug /Users/myname/ownCloud/GitHub/beets/custom/beetsplug
replace:
    '[\\/]': _
    ^\.: _
    '[\x00-\x1f]': _
    '[<>:"\?\*\|]': _
    \.$: _
    \s+$: ''
    ^\s+: ''
    ^-: _

match:
    strong_rec_thresh: 0.2
    max_rec:
        missing_tracks: low
        unmatched_tracks: low
    preferred:
        countries: [US, GB|UK]
        original_year: yes
musicbrainz:
    genre: no

# --------------- Plugins ---------------

plugins: fetchart
plugfiles:
    mode: stage
    format: $albumartist - $title
    temp_dir: /Volumes/share/tempmusic
scrub:
    auto: yes
embedart:
    compare_threshold: 0
fetchart:
    cover_names: front art album folder
    sources: filesystem coverart amazon albumart
    auto: yes
    minwidth: 0
    maxwidth: 0
    quality: 0
    max_filesize: 0
    enforce_ratio: no
    cautious: no
    store_source: no
    high_resolution: no
    deinterlace: no
    cover_format:
    google_key: REDACTED
    google_engine: REDACTED
    fanarttv_key: REDACTED
    lastfm_key: REDACTED
lyrics:
    auto: yes
    google_API_key: REDACTED
    google_engine_ID: REDACTED
    sources: google musixmatch genius tekstowo
lastgenre:
    auto: yes
    canonical: no
    count: 3
    fallback: none
    force: yes
    min_weight: 10
    prefer_specific: no
    source: album
    whitelist: yes
    title_case: yes
replaygain:
    auto: yes
    backend: ffmpeg
    per_disc: no
convert:
    auto: no
    copy_album_art: yes
    embed: yes
    never_convert_lossy_files: yes
    delete_originals: yes
missing:

# --------------- Search ---------------

    format_item: $album - $disc-$track $title
    count: no
    total: no

wcage03 avatar Jun 16 '25 21:06 wcage03

Hi, thank you for the report!

That is a good find. I just checked the relevant code and in theory we do resolve the pluginpath config option as string sequence.

Something like

pluginpath: [./path1, ./path2]

should work. That said, there is no test inplace for multiple plugin paths, so something could indeed be broken. Could you try again using the inline array syntax? I don't see why you original approach should not not work tho.

semohr avatar Jun 19 '25 09:06 semohr

Thanks for your assistance on this. I did a test. I moved the configuration to inline and it did not work in that format either. Let me know if I can do anything else to assist.

Bill

On Jun 19, 2025, at 4:16β€―AM, Sebastian Mohr @.***> wrote:

semohr left a comment (beetbox/beets#5825) https://github.com/beetbox/beets/issues/5825#issuecomment-2987358511 Hi, thank you for the report!

That is a good find. I just checked the relevant code https://github.com/beetbox/beets/blob/8fd20b9b6774509c7f1846d2cf3b3e56acea03ea/beets/ui/__init__.py#L1634 and in theory we do resolve the pluginpath config option as string sequence.

Something like

pluginpath: [./path1, ./path2] should work. That said, there is no test inplace for multiple plugin paths, so something could indeed be broken. Could you try again using the inline array syntax? I don't see why you original approach should not not work tho.

β€” Reply to this email directly, view it on GitHub https://github.com/beetbox/beets/issues/5825#issuecomment-2987358511, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFGESX34ULK65VRT6FZGQL3EJ5XPAVCNFSM6AAAAAB7OM5QQ2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDSOBXGM2TQNJRGE. You are receiving this because you authored the thread.

wcage03 avatar Jun 19 '25 13:06 wcage03

What was the yaml error in the first place? I have pluginpath in my config which works.

DjSlash avatar Jun 20 '25 05:06 DjSlash

The issue is about multiple pluginpaths. E.g. adding a folder with your own plugins. As mentioned above:

In all cases delivered plugins were found (e.g. the delivered beetsplug directory was referenced) and the custom plugin was not. It appears that other than confirming that the yaml is formatted, the pluginpath is not referenced at all.

There is no specific error raised sadly πŸ™ƒ

semohr avatar Jun 20 '25 09:06 semohr

The only yaml error occurs if you follow the format listed in the documentation (i.e. putting a hyphen in front of the value). I think that is just a documentation error. This is noted above.

The more important issue is that adding an additional plug in path value does not appear to be recognized. No error is generated, the program just appears to ignore the directive.

Bill Cage (205) 422-6081

On Fri, Jun 20, 2025, 4:07β€―AM Sebastian Mohr @.***> wrote:

semohr left a comment (beetbox/beets#5825) https://github.com/beetbox/beets/issues/5825#issuecomment-2990382443

The issue is about multiple pluginpaths. E.g. adding a folder with your own plugins. As mentioned above:

In all cases delivered plugins were found (e.g. the delivered beetsplug directory was referenced) and the custom plugin was not. It appears that other than confirming that the yaml is formatted, the pluginpath is not referenced at all.

There is no specific error sadly πŸ™ƒ

β€” Reply to this email directly, view it on GitHub https://github.com/beetbox/beets/issues/5825#issuecomment-2990382443, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFGESVB3WBI4AJIGGAISWL3EPFLNAVCNFSM6AAAAAB7OM5QQ2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDSOJQGM4DENBUGM . You are receiving this because you authored the thread.Message ID: @.***>

wcage03 avatar Jun 21 '25 00:06 wcage03

Okay. @wcage03 says there is a error, @semohr says there isn't. Since @wcage03 is the OP, I guess that they saw the error and I'm curious what that error is.

As said, I have pluginpath in my config with multiple paths with hyphens in front of them and that works.

beet config converts that into a string list, so that's also a possible way to use it (as stated earlier in the thread).

But, what I see in the config in OP's initial post, I see a syntax where I don't have much faith of it working.


for reference, in my config:

pluginpath:                                                                                                                                                                                                                                    
    - ~/.local/share/pipx/venvs/beets-xtractor/lib/python3.13/site-packages/beetsplug/                                                                                                                                                         
    - ~/.local/share/venv/lib/python3.13/site-packages/beetsplug/                                                                                                                                                                              
    - ~/gitzooi/whatlastgenre/plugin/beets/beetsplug 

beet config output:

pluginpath: [~/.local/share/pipx/venvs/beets-xtractor/lib/python3.13/site-packages/beetsplug/, ~/.local/share/venv/lib/python3.13/site-packages/beetsplug/, ~/gitzooi/whatlastgenre/plugin/beets/beetsplug]

DjSlash avatar Jun 22 '25 13:06 DjSlash

It would probably be useful to see the verbose log (beet -vv ...) of any command that fails to use pluginpath.

Also, as far as I can tell, you will still need to enable those plugins, which you don't seem to do:

plugins: fetchart  # <- no custom plugins enabled here

wisp3rwind avatar Jun 23 '25 23:06 wisp3rwind

This is actually pretty simple for me to recreate. I installed a clean latest version of beets in an "editable" configuration using the command below (as documented). pip install -e git+https://github.com/beetbox/beets#egg=beets

No mods, no adds, nothing but delivered code. I have one custom plugin that I developed. I usually stick it in the source directory beetsplug where all the delivered plugins live. It works fine for my purposes. I noticed in the documentation that you can specify multiple plugin paths. This is attractive so that I can keep my custom code separate. I gave it a shot. As noted in the original post, I used the format:

   pluginpath: 
       - /Users/myname/ownCloud/GitHub/beets/src/beets/beetsplug
       - /Users/myname/ownCloud/GitHub/beets/custom/beetsplug

As documented (with the preceding hyphens). The first is the path to the delivered plugins and the second is a path to my custom code. When I use the hyphens, I get this error when I run the command "beet version":

beet version
configuration error: paths.pluginpath: must be a string

When I remove the hyphens, I get a normal response from beet version, e.g.

beet version
beets version 2.3.1
Python version 3.12.2
plugins: badfiles, convert, embedart, fetchart, info, lastgenre, lyrics, mbsync, missing, musicbrainz, replaygain, scrub, unimported

Note: the specific plugins that are included is irrelevant and does not impact the outcome of the test. Without an error, I assume at this point that Beets is configured to look at my pluginpath(s) now. Adding my custom plugin to the plugins: line in the config, I get the following when I run beet version:

beet version
** error loading plugin plugfiles:
Traceback (most recent call last):
  File "/Users/myname/ownCloud/GitHub/beets/src/beets/beets/plugins.py", line 361, in load_plugins
    namespace = __import__(modname, None, None)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ModuleNotFoundError: No module named 'beetsplug.plugfiles'

Note: my custom plugin is named plugfiles. If I copy my custom plugin into the source beetsplug directory, then everything works (my custom plugin is found and executes as expected). Clearly, it is not looking in my custom plugin path. For completeness, I ran an ls command on the custom path to make sure that I did not make a typo on the path definition:

ls /Users/myname/ownCloud/GitHub/beets/custom/beetsplug
plugfiles.py

My plugin is where I tried to tell beets to look. I very well could be doing something wrong, but the test case that I am using seems to be simple and straight to a demonstration of the intended functionality. That said, I am not going to lose sleep over this since all I have to do is copy my custom plugin into the delivered directory and I am golden, but it appears to be an error in the code and/or an error in the documentation. Happy to help in anyway that I can.

wcage03 avatar Jun 25 '25 02:06 wcage03

I just tried to reproduce this and I failed to do so. I'm loading plugins from different folders without any issue Here my config (notice the myawesomeplugin line)

 # config.yaml
...
plugins:
    [
        info,
        the,
        fetchart,
        embedart,
        ftintitle,
        lastgenre,
        missing,
        albumtypes,
        scrub,
        zero,
        mbsync,
        duplicates,
        convert,
        fromfilename,
        importfeeds,
        inline,
        edit,
        spotify,
        plexupdate,
        myawesomeplugin
    ]

pluginpath: 
    - /mnt/Repositories/test/beetsplug
    - /mnt/Repositories/beets/beetsplug
...

The plugin looks as follows:

from beets.plugins import BeetsPlugin

def loaded():
    print("My plugin loaded!")

class MyPlugin(BeetsPlugin):
    def __init__(self):
        super().__init__()
        self.register_listener("pluginload", loaded)

beet version gives the following output showing the plugin was loaded successfully:

My plugin loaded!
beets version 2.3.1
Python version 3.12.11
plugins: albumtypes, convert, duplicates, edit, embedart, fetchart, fromfilename, ftintitle, importfeeds, info, inline, lastgenre, mbsync, missing, myawesomeplugin, plexupdate, scrub, spotify, the, zero

@wcage03 Maybe your plugin is malformed?

semohr avatar Jun 26 '25 10:06 semohr

The plugin works perfectly fine when it is in the main folder so there is no problem with the plugin itself. It is just not found for me via the plluginpath directive. The only possible issue would be dependencies within the plugin where it relies on other existing plugin code. I doubt this is the problem and logically it would not make sense that any existing dependencies would have to be replicated into the secondary path.

Since I am doing a complete clean install (as described in my post) could it be a path directive or other python directive that is solving the path issue for you and it is not actually being picked up from the pluginpath directive? It would be interesting to see if you removed your β€œtest” path from your config and testing to see if your install continues to find your plugin.

As I said previously, this was something that I noted and I can replicate on my machine so I posted an issue. It does not cause me great heartburn since I can just copy my single custom plugin to the folder.

I do appreciate your persistence in following up on this.

On Jun 26, 2025, at 5:55β€―AM, Sebastian Mohr @.***> wrote:

semohr left a comment (beetbox/beets#5825) https://github.com/beetbox/beets/issues/5825#issuecomment-3008066491 I just tried to reproduce this and I failed to do so. I'm loading plugins from different folders without any issue Here my config (notice the myawesomeplugin line)

config.yaml

... plugins: [ info, the, fetchart, embedart, ftintitle, lastgenre, missing, albumtypes, scrub, zero, mbsync, duplicates, convert, fromfilename, importfeeds, inline, edit, spotify, plexupdate, myawesomeplugin ]

pluginpath: - /mnt/Repositories/test/beetsplug - /mnt/Repositories/beets/beetsplug ... The plugin looks as follows:

from beets.plugins import BeetsPlugin

def loaded(): print("My plugin loaded!")

class MyPlugin(BeetsPlugin): def init(self): super().init() self.register_listener("pluginload", loaded) beet version gives the following output showing the plugin was loaded successfully:

My plugin loaded! beets version 2.3.1 Python version 3.12.11 plugins: albumtypes, convert, duplicates, edit, embedart, fetchart, fromfilename, ftintitle, importfeeds, info, inline, lastgenre, mbsync, missing, myawesomeplugin, plexupdate, scrub, spotify, the, zero @wcage03 https://github.com/wcage03 Maybe your plugin is malformed?

β€” Reply to this email directly, view it on GitHub https://github.com/beetbox/beets/issues/5825#issuecomment-3008066491, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAFGESUPKZPPRWA3WGLQ7T33FPGQNAVCNFSM6AAAAAB7OM5QQ2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTAMBYGA3DMNBZGE. You are receiving this because you were mentioned.

wcage03 avatar Jun 28 '25 18:06 wcage03

If I remove the test pluginpath the plugin is not found anymore (as expected). For me everything seems to be in order πŸ€”

** error loading plugin myawesomeplugin:
Traceback (most recent call last):
  File "/mnt/Repositories/confuse/src/beets/beets/plugins.py", line 361, in load_plugins
    namespace = __import__(modname, None, None)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ModuleNotFoundError: No module named 'beetsplug.myawesomeplugin'

beets version 2.3.1
Python version 3.12.11
plugins: albumtypes, convert, duplicates, edit, embedart, fetchart, fromfilename, ftintitle, importfeeds, info, inline, lastgenre, mbsync, missing, plexupdate, scrub, spotify, the, zero

As I said previously, this was something that I noted and I can replicate on my machine so I posted an issue. It does not cause me great heartburn since I can just copy my single custom plugin to the folder.

Would be nice if I could replicate it too tho, otherwise I'm not really able to fix it πŸ™ƒ

semohr avatar Jul 01 '25 08:07 semohr