--- /dev/null
+.. SPDX-FileCopyrightText: 2018-2023 kaliko <kaliko@azylum.org>
+.. SPDX-License-Identifier: LGPL-3.0-or-later
+
+.. _examples:
+
+Examples
+========
+
+Plain examples
+--------------
+
+Connect, if playing, get currently playing track, the next one:
+
+.. literalinclude:: examples/connect.py
+ :language: python
+ :linenos:
+
+Connect a specific password protected host:
+
+.. literalinclude:: examples/connect_host.py
+ :language: python
+ :linenos:
+
+Start playing current queue and set the volume:
+
+.. literalinclude:: examples/playback.py
+ :language: python
+ :linenos:
+
+Clear the queue, search artist, queue what's found and play:
+
+.. literalinclude:: examples/findadd.py
+ :language: python
+ :linenos:
+
+Object Oriented example
+-----------------------
+
+A plain client monitoring changes on MPD.
+
+.. literalinclude:: examples/client.py
+ :language: python
+ :linenos:
+
--- /dev/null
+"""Plain client class
+"""
+import logging
+import select
+import sys
+
+import musicpd
+
+
+class MyClient(musicpd.MPDClient):
+ """Plain client inheriting from MPDClient"""
+
+ def __init__(self):
+ # Set logging to debug level
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(levelname)-8s %(message)s')
+ self.log = logging.getLogger(__name__)
+ super().__init__()
+ # Set host/port/password after init to overrides defaults
+ # self.host = 'example.org'
+ # self.port = 4242
+ # self.pwd = 'secret'
+
+ def connect(self):
+ """Overriding explicitly MPDClient.connect()"""
+ try:
+ super().connect(host=self.host, port=self.port)
+ if hasattr(self, 'pwd') and self.pwd:
+ self.password(self.pwd)
+ except musicpd.ConnectionError as err:
+ # Catch socket error
+ self.log.error('Failed to connect: %s', err)
+ sys.exit(42)
+
+ def _wait_for_changes(self, callback):
+ select_timeout = 10 # second
+ while True:
+ self.send_idle() # use send_ API to avoid blocking on read
+ _read, _, _ = select.select([self], [], [], select_timeout)
+ if _read: # tries to read response
+ ret = self.fetch_idle()
+ # do something
+ callback(ret)
+ else: # cancels idle
+ self.noidle()
+
+ def callback(self, *args):
+ """Method launch on MPD event, cf. monitor method"""
+ self.log.info('%s', args)
+
+ def monitor(self):
+ """Continuously monitor MPD activity.
+ Launch callback method on event.
+ """
+ try:
+ self._wait_for_changes(self.callback)
+ except (OSError, musicpd.MPDError) as err:
+ self.log.error('%s: Something went wrong: %s',
+ type(err).__name__, err)
+
+if __name__ == '__main__':
+ cli = MyClient()
+ # You can overrides host here or in init
+ #cli.host = 'example.org'
+ # Connect MPD server
+ try:
+ cli.connect()
+ except musicpd.ConnectionError as err:
+ cli.log.error(err)
+
+ # Monitor MPD changes, blocking/timeout idle approach
+ try:
+ cli.socket_timeout = 20 # seconds
+ ret = cli.idle()
+ cli.log.info('Leaving idle, got: %s', ret)
+ except TimeoutError as err:
+ cli.log.info('Nothing occured the last %ss', cli.socket_timeout)
+
+ # Reset connection
+ try:
+ cli.socket_timeout = None
+ cli.disconnect()
+ cli.connect()
+ except musicpd.ConnectionError as err:
+ cli.log.error(err)
+
+ # Monitor MPD changes, non blocking idle approach
+ try:
+ cli.monitor()
+ except KeyboardInterrupt as err:
+ cli.log.info(type(err).__name__)
+ cli.send_noidle()
+ cli.disconnect()
+
--- /dev/null
+import musicpd
+import logging
+
+import musicpd
+
+# Set logging to debug level
+# it should log messages showing where defaults come from
+logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s')
+log = logging.getLogger()
+
+client = musicpd.MPDClient()
+# use MPD_HOST/MPD_PORT env var if set else
+# test ${XDG_RUNTIME_DIR}/mpd/socket for existence
+# fallback to localhost:6600
+# connect support host/port argument as well
+client.connect()
+
+status = client.status()
+if status.get('state') == 'play':
+ current_song_id = status.get('songid')
+ current_song = client.playlistid(current_song_id)[0]
+ log.info(f'Playing : {current_song.get("file")}')
+ next_song_id = status.get('nextsongid', None)
+ if next_song_id:
+ next_song = client.playlistid(next_song_id)[0]
+ log.info(f'Next song : {next_song.get("file")}')
+else:
+ log.info('Not playing')
+
+client.disconnect()
--- /dev/null
+import sys
+import logging
+
+import musicpd
+
+# Set logging to debug level
+logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s')
+
+client = musicpd.MPDClient()
+try:
+ client.connect(host='example.lan')
+ client.password('secret')
+ client.status()
+except musicpd.MPDError as err:
+ print(f'An error occured: {err}')
+finally:
+ client.disconnect()
--- /dev/null
+import musicpd
+
+# Using a context manager
+# (use env var if you need to override default host)
+with musicpd.MPDClient() as client:
+ client.clear()
+ client.findadd("(artist == 'Monkey3')")
+ client.play()
--- /dev/null
+import musicpd
+
+# Using a context manager
+# (use env var if you need to override default host)
+with musicpd.MPDClient() as client:
+ client.play()
+ client.setvol('80')
use.rst
doc.rst
commands.rst
+ examples.rst
contribute.rst
written to the socket. To avoid confusion use regular string instead of relying
on object string representation.
-:py:class:`musicpd.MPDClient` methods returns different kinds of objects depending on the command. Could be :py:obj:`None`, a single object as a :py:obj:`str` or a :py:obj:`dict`, a list of :py:obj:`dict`.
+:py:class:`musicpd.MPDClient` methods returns different kinds of objects
+depending on the command. Could be :py:obj:`None`, a single object as a
+:py:obj:`str` or a :py:obj:`dict`, a list of :py:obj:`dict`.
-For more about the protocol and MPD commands see the `MPD protocol
-documentation`_.
+Then :py:class:`musicpd.MPDClient` **methods signatures** are not hard coded
+within this module since the protocol is handled on the server side. Please
+refer to the protocol and MPD commands in `MPD protocol documentation`_ to
+learn how to call commands and what kind of arguments they expect.
+
+Some examples are provided for the most common cases, see :ref:`examples`.
For a list of currently supported commands in this python module see
:ref:`commands`.