Tutorials

Install

For now the code is in early development stage. No releases are made.

# Use a virtualenv
python -m venv .../path/to/my/venv
. .../path/to/my/venv/bin/activate
pip install git+https://codeberg.org/MusicPlayerDaemon/python-musicpdaio.git@main

Getting started

Connect, clear the queue, add tracks

import mpdaio

client = mpdaio.MPDClient()
await client.ping()         # Plain connection test
print(client.version)       # Prints MPD's protocol version
print(await client.clear()) # Clears the current queue
# Add all tracks from artist "Amon Tobin"
print(await client.searchadd('(Artist == "Amon Tobin")'))
await client.play()
await client.setvol(60)
print(await client.currentsong())
await client.close()        # Finally close connection

musicpdaio tries to come with sane defaults, then running mpdaio.MPDClient with no explicit argument will try default values to connect to MPD. Cf. Reference for more about defaults.

Using a specific host, port and a password.

The password is sent when a connection is made, no need to explicitly use the password command. In the following code a client is constructed with a password argument, then when the ping method is called:

  • the client fetch a connection from the pool

  • then a password command is sent with the password

  • finally the ping command is sent.

client = mpdaio.MPDClient(host='example.org', port='6601', password='53(237')
await client.ping()

Wrapping some commands in a python script

# mpd-client.py
import asyncio
import logging

from mpdaio import MPDClient

# Configure loggers
logging.basicConfig(level=logging.INFO, format='%(levelname)-8s %(message)s')
logging.getLogger("asyncio").setLevel(logging.WARNING)
# debug level level will show where defaults settings come from
log = logging.getLogger('mpdaio.client')
log.setLevel(logging.DEBUG)


async def run():
    # Explicit host declaration
    #client = MPDClient(host='example.org', port='6601')

    # Use defaults
    client = MPDClient()
    # MPDClient use MPD_HOST/MPD_PORT env var if set
    # else test ${XDG_RUNTIME_DIR}/mpd/socket for existence
    # finnally fallback to localhost:6600

    # Make an initial connection to MPD server
    # The connection is kept open an reused for later commands
    await client.ping()

    # Get player status
    status = await client.status()
    if status.get('state') == 'play':
        current_song_id = status.get('songid')
        current_song = await client.playlistid(current_song_id)
        log.info(f'Playing   : {current_song[0].get("file")}')
        next_song_id = status.get('nextsongid', None)
        if next_song_id:
            next_song = await client.playlistid(next_song_id)
            log.info(f'Next song : {next_song[0].get("file")}')
    else:
        log.info('Not playing')

    # Add all songs form artist "The Doors"
    await client.searchadd('(Artist == "The Doors")')
    # Start playing
    if (await client.status()).get('state') != 'play':
        await client.play()


    # Closes any remaining connections to MPD server
    await client.close()


if __name__ == '__main__':
    asyncio.run(run())

Fetch album art for the given track

The logic is similar with readpicture command.

client = mpdaio.MPDClient()
# Looking for cover art in 'Tool/2001-Lateralus/'
track = 'Tool/2001-Lateralus/09-Tool - Lateralus.flac'
aart = await cli.albumart(track, 0)
received = int(aart.get('binary'))
size = int(aart.get('size'))
with open('/tmp/cover', 'wb') as cover:
    # aart = {'size': 42, 'binary': 2051, data: bytes(...)}
    cover.write(aart.get('data'))
    while received < size:
        aart = await cli.albumart(track, received)
        cover.write(aart.get('data'))
        received += int(aart.get('binary'))
    if received != size:
        print('something went wrong')
await cli.close()

Cf. MPD protocol documentation for more binary responses.

Concurrency

import asyncio
import logging

from mpdaio import MPDClient

# Configure loggers
logging.basicConfig(level=logging.INFO, format='%(levelname)-8s %(message)s')
logging.getLogger("asyncio").setLevel(logging.WARNING)
# debug level level will show where defaults settings come from
log = logging.getLogger('mpdaio.client')
log.setLevel(logging.DEBUG)


async def search(fltr):
    # Look for and add
    await client.searchadd(fltr)


async def run():

    # Make an initial connection to MPD server
    # The connection is kept open an reused for later commands
    await client.ping()
    await client.clear()
    filters = [
            '(Artist == "Neurosis")',
            '(Artist == "Isis")',
            '(Artist == "Cult of Luna")',
            ]
    # Each task gathered here will run with it's own connection
    await asyncio.gather(*map(search, filters))

    # Closes all connections to MPD server
    await client.close()


if __name__ == '__main__':
    # Use defaults to access MPD server
    client = MPDClient()
    asyncio.run(run())