How to Add Files to mpd using python-mpd2

The music player daemon, mpd, and its client counterpart mpc operate on a managed directory structure. All paths are relative to this root directory. You cannot make mpd play a file from just anywhere, it seems.

This is important to know when you script MPDClient using python-mpd2, because when you try to add any absolute path, even those pointing into the managed directory, you’ll be in trouble.

mpd.base.CommandError: [4@0] {add} Access denied

That’s all you’ll get for an absolute file://-style URL.

And if you just use the absolute path without a protocol specification, you get:

mpd.base.CommandError: [50@0] {add} Malformed URI

The trick is to remember to exclusively use relative paths.

Instead of e.g.

/music/mpd/files/The Artist/The Album/The Track.mp3`

you pass in

The Artist/The Album/The Track.mp3

and all will be fine.

To make sure you get this right, have a look at the output of mpc listall where all accepted relative file paths are listed.

You can use os.path.relpath to remove the path to the managed music library from an absolute path. See the following classes for an example.

Update 2021-03-10: Reader logicalextreme pointed out that I might be holding the tool wrong! Could be that environment varaibles aren’t set up properly, and changing the connection call a bit might help. I wasn’t able to verify this, yet, but here’s the advice he’s giving:

If you’re connecting to a local mpd instance, but via a hostname and port, check your MPD_HOST and MPD_PORT environment variables for what mpc connects to by default. My MPD_HOST was /run/mpd/socket and the port was empty, and connecting with client.connect(“/run/mpd/socket”) eliminated the access denied error for me (I’m adding stuff that’s definitely outside mpd’s library directories, and I’ve been doing it via mpc for years).

Obviously if you’re not adding anything from outside mpd’s library then this won’t be very useful to you, but you might want to update the post cause it’s one of the only two relevant search results I found for the issue!

Types to manage music libraries

import os

class Album:
    def __init__(self, rel_path):
        self.rel_path = rel_path

    def name(self):
        return os.path.basename(self.rel_path)

class Library:
    def __init__(self, lib_path):
        self.path = os.path.abspath(lib_path)

    def _guard_exists(self):
        if not (os.path.exists(self.path) \
                and os.path.isdir(self.path)):
            raise Exception("Music library directory does not exist at: %s" % self.path)

    def all_albums(self):
        """Returns a list of Album objects in the library subdirectories, sorted by name."""
        return [Album(rel_path=path)
                for path in sorted(os.listdir(self.path))
                if os.path.isdir(os.path.join(self.path, path))]

I then use Album objects to make the audio player switch audio books.