# Examples

## Plain examples

Connect, if playing, get currently playing track, the next one:

     1import logging
     2
     3import musicpd
     4
     5# Set logging to debug level
     6# it should log messages showing where defaults come from
     7logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s')
     8log = logging.getLogger()
     9
    10client = musicpd.MPDClient()
    11# use MPD_HOST/MPD_PORT env var if set else
    12# test ${XDG_RUNTIME_DIR}/mpd/socket for existence
    13# fallback to localhost:6600
    14# connect support host/port argument as well
    15client.connect()
    16
    17status = client.status()
    18if status.get('state') == 'play':
    19    current_song_id = status.get('songid')
    20    current_song = client.playlistid(current_song_id)[0]
    21    log.info(f'Playing   : {current_song.get("file")}')
    22    next_song_id = status.get('nextsongid', None)
    23    if next_song_id:
    24        next_song = client.playlistid(next_song_id)[0]
    25        log.info(f'Next song : {next_song.get("file")}')
    26else:
    27    log.info('Not playing')
    28
    29client.disconnect()

Connect a specific password protected host:

     1import sys
     2import logging
     3
     4import musicpd
     5
     6# Set logging to debug level
     7logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s')
     8
     9client = musicpd.MPDClient()
    10try:
    11    client.connect(host='example.lan')
    12    client.password('secret')
    13    client.status()
    14except musicpd.MPDError as err:
    15    print(f'An error occured: {err}')
    16finally:
    17    client.disconnect()

Start playing current queue and set the volume:

    1import musicpd
    2
    3# Using a context manager
    4# (use env var if you need to override default host)
    5with musicpd.MPDClient() as client:
    6    client.play()
    7    client.setvol('80')

Clear the queue, search artist, queue what’s found and play:

    1import musicpd
    2
    3# Using a context manager
    4# (use env var if you need to override default host)
    5with musicpd.MPDClient() as client:
    6    client.clear()
    7    client.findadd("(artist == 'Monkey3')")
    8    client.play()

## Object Oriented example

A plain client monitoring changes on MPD.

     1"""Plain client class
     2"""
     3import logging
     4import select
     5import sys
     6
     7import musicpd
     8
     9
    10class MyClient(musicpd.MPDClient):
    11    """Plain client inheriting from MPDClient"""
    12
    13    def __init__(self):
    14        # Set logging to debug level
    15        logging.basicConfig(level=logging.DEBUG,
    16                            format='%(levelname)-8s %(module)-8s %(message)s')
    17        self.log = logging.getLogger(__name__)
    18        super().__init__()
    19        # Set host/port/password after init to overrides defaults
    20        # self.host = 'example.org'
    21        # self.port = 4242
    22        # self.pwd = 'secret'
    23
    24    def connect(self):
    25        """Overriding explicitly MPDClient.connect()"""
    26        try:
    27            super().connect(host=self.host, port=self.port)
    28            if hasattr(self, 'pwd') and self.pwd:
    29                self.password(self.pwd)
    30        except musicpd.ConnectionError as err:
    31            # Catch socket error
    32            self.log.error('Failed to connect: %s', err)
    33            sys.exit(42)
    34
    35    def _wait_for_changes(self, callback):
    36        select_timeout = 10  # second
    37        while True:
    38            self.send_idle()  # use send_ API to avoid blocking on read
    39            _read, _, _ = select.select([self], [], [], select_timeout)
    40            if _read:  # tries to read response
    41                ret = self.fetch_idle()
    42                # do something
    43                callback(ret)
    44            else:  # cancels idle
    45                self.noidle()
    46
    47    def callback(self, *args):
    48        """Method launch on MPD event, cf. monitor method"""
    49        self.log.info('%s', args)
    50
    51    def monitor(self):
    52        """Continuously monitor MPD activity.
    53        Launch callback method on event.
    54        """
    55        try:
    56            self._wait_for_changes(self.callback)
    57        except (OSError, musicpd.MPDError) as err:
    58            self.log.error('%s: Something went wrong: %s',
    59                           type(err).__name__, err)
    60
    61if __name__ == '__main__':
    62    cli = MyClient()
    63    # You can overrides host here or in init
    64    #cli.host = 'example.org'
    65    # Connect MPD server
    66    try:
    67        cli.connect()
    68    except musicpd.ConnectionError as err:
    69        cli.log.error(err)
    70
    71    # Monitor MPD changes, blocking/timeout idle approach
    72    try:
    73        cli.socket_timeout =  20 # seconds
    74        ret = cli.idle()
    75        cli.log.info('Leaving idle, got: %s', ret)
    76    except TimeoutError as err:
    77        cli.log.info('Nothing occured the last %ss', cli.socket_timeout)
    78
    79    # Reset connection
    80    try:
    81        cli.socket_timeout = None
    82        cli.disconnect()
    83        cli.connect()
    84    except musicpd.ConnectionError as err:
    85        cli.log.error(err)
    86
    87    # Monitor MPD changes, non blocking idle approach
    88    try:
    89        cli.monitor()
    90    except KeyboardInterrupt as err:
    91        cli.log.info(type(err).__name__)
    92        cli.send_noidle()
    93        cli.disconnect()
    94

## Dealing with Exceptions

Musicpd module will raise it’s own `MPDError` exceptions and python `OSError`. Then you can wrap `OSError` in `MPDError` exceptions to have to deal with a single type of exceptions in your code:

     1"""client class dealing with all Exceptions
     2"""
     3import logging
     4
     5import musicpd
     6
     7
     8# Wrap Exception decorator
     9def wrapext(func):
    10    """Decorator to wrap errors in musicpd.MPDError"""
    11    errors=(OSError, TimeoutError)
    12    into = musicpd.MPDError
    13    def w_func(*args, **kwargs):
    14        try:
    15            return func(*args, **kwargs)
    16        except errors as err:
    17            strerr = str(err)
    18            if hasattr(err, 'strerror'):
    19                if err.strerror:
    20                    strerr = err.strerror
    21            raise into(strerr) from err
    22    return w_func
    23
    24
    25class MyClient(musicpd.MPDClient):
    26    """Plain client inheriting from MPDClient"""
    27
    28    def __init__(self):
    29        # Set logging to debug level
    30        logging.basicConfig(level=logging.DEBUG,
    31                format='%(levelname)-8s %(module)-10s %(message)s')
    32        self.log = logging.getLogger(__name__)
    33        super().__init__()
    34
    35    @wrapext
    36    def __getattr__(self, cmd):
    37        """Wrapper around MPDClient calls for abstract overriding"""
    38        self.log.debug('cmd: %s', cmd)
    39        return super().__getattr__(cmd)
    40
    41
    42if __name__ == '__main__':
    43    cli = MyClient()
    44    # You can overrides host here or in init
    45    #cli.host = 'example.org'
    46    # Connect MPD server
    47    try:
    48        cli.connect()
    49        cli.currentsong()
    50        cli.stats()
    51    except musicpd.MPDError as error:
    52        cli.log.fatal(error)
    53    finally:
    54        cli.log.info('Disconnecting')
    55        try:
    56            # Tries to close the socket anyway
    57            cli.disconnect()
    58        except OSError:
    59            pass