-Python MusicPlayerDaemon client module
-***************************************
+Music Player Daemon client module
+*********************************
An MPD (Music Player Daemon) client library written in pure Python.
:Dependencies: None
:Compatibility: Python 3.6+
:Licence: GNU LGPLv3
+
+----
-.. SPDX-FileCopyrightText: 2018-2021 kaliko <kaliko@azylum.org>
+.. SPDX-FileCopyrightText: 2018-2023 kaliko <kaliko@azylum.org>
.. SPDX-License-Identifier: LGPL-3.0-or-later
.. _commands:
import musicpd
print(' '.join([cmd for cmd in musicpd.MPDClient()._commands.keys()]))
-
-List, last updated for v0.6.0:
+List, last updated for v0.8.0:
.. literalinclude:: commands.txt
stats -> fetch_object
== Playback Option Commands
-consume <bool> -> fetch_nothing
+consume <str> -> fetch_nothing
crossfade <int> -> fetch_nothing
mixrampdb <str> -> fetch_nothing
mixrampdelay <int> -> fetch_nothing
notcommands -> fetch_list
urlhandlers -> fetch_list
decoders -> fetch_plugins
+
+== Client to Client
+subscribe <str> -> self._fetch_nothing,
+unsubscribe <str> -> self._fetch_nothing,
+channels -> self._fetch_list,
+readmessages -> self._fetch_messages,
+sendmessage <str> <str> -> self._fetch_nothing,
-.. SPDX-FileCopyrightText: 2018-2021 kaliko <kaliko@azylum.org>
+.. SPDX-FileCopyrightText: 2018-2023 kaliko <kaliko@azylum.org>
.. SPDX-License-Identifier: LGPL-3.0-or-later
musicpd namespace
=================
-.. autodata:: musicpd.CONNECTION_TIMEOUT
-
-.. autodata:: musicpd.SOCKET_TIMEOUT
-
-.. autoclass:: musicpd.MPDClient
- :members:
+.. automodule:: musicpd
+ :members:
+ :no-undoc-members:
.. vim: spell spelllang=en
-.. SPDX-FileCopyrightText: 2018-2021 kaliko <kaliko@azylum.org>
+.. SPDX-FileCopyrightText: 2018-2023 kaliko <kaliko@azylum.org>
.. SPDX-License-Identifier: LGPL-3.0-or-later
.. include:: ../../README.rst
Library overview
-----------------
+================
Here is a snippet allowing to list the last modified artists in the media library:
.. code:: python3
Build documentation
---------------------
+===================
.. code:: bash
.. toctree::
:maxdepth: 2
+ self
use.rst
doc.rst
commands.rst
# test ${XDG_RUNTIME_DIR}/mpd/socket for existence
# fallback to localhost:6600
# connect support host/port argument as well
- print(client.mpd_version) # print the mpd protocol version
- print(client.cmd('foo', 42)) # print result of the request "cmd foo 42"
- # (nb. for actual command, see link to the protocol below)
+ print(client.mpd_version) # print the MPD protocol version
+ client.setvol('42') # sets the volume
client.disconnect() # disconnect from the server
-In the example above `cmd` in not an actual MPD command, for a list of
-supported commands, their arguments (as MPD currently understands
-them), and the functions used to parse their responses see :ref:`commands`.
+The MPD command protocol exchanges line-based text records. The client emits a
+command with optional arguments. In the example above the client sends a
+`setvol` command with the string argument `42`.
-See the `MPD protocol documentation`_ for more details.
+MPD commands are exposed as :py:class:`musicpd.MPDClient` methods. Methods
+**arguments are python strings**. Some commands are composed of more than one word
+(ie "**tagtypes [disable|enable|all]**"), for these use a `snake case`_ style to
+access the method. Then **"tagtypes enable"** command is called with
+**"tagtypes_enable"**.
+
+Remember MPD protocol is text based, then all MPD command arguments are UTF-8
+strings. In the example above, an integer can be used as argument for the
+`setvol` command, but it is then evaluated as a string when the command is
+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`.
+
+For more about the protocol and MPD commands see the `MPD protocol
+documentation`_.
+
+For a list of currently supported commands in this python module see
+:ref:`commands`.
.. _environment_variables:
Environment variables
---------------------
-The client honors the following environment variables:
+:py:class:`musicpd.MPDClient` honors the following environment variables:
+
+.. envvar:: MPD_HOST
+
+ MPD host (:abbr:`FQDN (fully qualified domain name)`, IP, socket path or abstract socket) and password.
- * ``MPD_HOST`` MPD host (:abbr:`FQDN (fully qualified domain name)`, socket path or abstract socket) and password.
+ | To define a **password** set :envvar:`MPD_HOST` to "*password@host*" (password only "*password@*")
+ | For **abstract socket** use "@" as prefix : "*@socket*" and then with a password "*pass@@socket*"
+ | Regular **unix socket** are set with an absolute path: "*/run/mpd/socket*"
- | To define a password set MPD_HOST to "`password@host`" (password only "`password@`")
- | For abstract socket use "@" as prefix : "`@socket`" and then with a password "`pass@@socket`"
- | Regular unix socket are set with an absolute path: "`/run/mpd/socket`"
- * ``MPD_PORT`` MPD port, relevant for TCP socket only, ie with :abbr:`FQDN (fully qualified domain name)` defined host
- * ``MPD_TIMEOUT`` timeout for connecting to MPD and waiting for MPD’s response in seconds
- * ``XDG_RUNTIME_DIR`` path to look for potential socket: ``${XDG_RUNTIME_DIR}/mpd/socket``
+.. envvar:: MPD_PORT
+
+ MPD port, relevant for TCP socket only
+
+.. envvar:: MPD_TIMEOUT
+
+ socket timeout when connecting to MPD and waiting for MPD’s response (in seconds)
+
+.. envvar:: XDG_RUNTIME_DIR
+
+ path to look for potential socket
.. _default_settings:
Default settings
----------------
- * If ``MPD_HOST`` is not set, then look for a socket in ``${XDG_RUNTIME_DIR}/mpd/socket``
- * If there is no socket use ``localhost``
- * If ``MPD_PORT`` is not set, then use ``6600``
- * If ``MPD_TIMEOUT`` is not set, then uses :py:obj:`musicpd.CONNECTION_TIMEOUT`
+Default host:
+ * use :envvar:`MPD_HOST` environment variable if set, extract password if present,
+ * else looks for an existing file in :envvar:`${XDG_RUNTIME_DIR:-/run/}/mpd/socket`
+ * else set host to ``localhost``
+
+Default port:
+ * use :envvar:`MPD_PORT` environment variable is set
+ * else use ``6600``
+Default timeout:
+ * use :envvar:`MPD_TIMEOUT` is set
+ * else use :py:obj:`musicpd.CONNECTION_TIMEOUT`
Context manager
---------------
Possible ranges are: `"START:END"`, `"START:"` and `":"` :
-Instead of giving the plain string as `"START:END"`, you **can** provide a :py:obj:`tuple` as `(START,END)`. The module is then ensuring the format is correct and raises an :py:obj:`musicpd.CommandError` exception otherwise. Empty start or end can be specified as en empty string `''` or :py:obj:`None`.
+Instead of giving the plain string as `"START:END"`, you **can** provide a :py:obj:`tuple` as `(START,END)`. The module is then ensuring the format is correct and raises an :py:obj:`musicpd.CommandError` exception otherwise. Empty start or end can be specified as en empty string ``''`` or :py:obj:`None`.
.. code-block:: python
# missing end interpreted as highest value possible, pay attention still need a tuple.
client.delete((pos,)) # purge queue from current to the end
-A notable case is the `rangeid` command allowing an empty range specified
-as a single colon as argument (i.e. sending just ":"):
+A notable case is the *rangeid* command allowing an empty range specified
+as a single colon as argument (i.e. sending just ``":"``):
.. code-block:: python
Empty start in range (i.e. ":END") are not possible and will raise a CommandError.
-Remember the of the tuple is optional, range can still be specified as single string `START:END`. In case of malformed range a CommandError is still raised.
+Remember the of the tuple is optional, range can still be specified as single string ``"START:END"``.
Iterators
----------
.. _MPD protocol documentation: http://www.musicpd.org/doc/protocol/
+.. _snake case: https://en.wikipedia.org/wiki/Snake_case
.. vim: spell spelllang=en
+# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: 2012-2023 kaliko <kaliko@azylum.org>
# SPDX-FileCopyrightText: 2021 Wonko der Verständige <wonko@hanstool.org>
# SPDX-FileCopyrightText: 2019 Naglis Jonaitis <naglis@mailbox.org>
# SPDX-FileCopyrightText: 2019 Bart Van Loon <bbb@bbbart.be>
# SPDX-FileCopyrightText: 2008-2010 J. Alexander Treuman <jat@spatialrift.net>
# SPDX-License-Identifier: LGPL-3.0-or-later
-"""python-musicpd: Python Music Player Daemon client library"""
+"""Python Music Player Daemon client library"""
import socket
ERROR_PREFIX = "ACK "
SUCCESS = "OK"
NEXT = "list_OK"
+#: Module version
VERSION = '0.9.0b0'
#: Seconds before a connection attempt times out
-#: (overriden by MPD_TIMEOUT env. var.)
+#: (overriden by :envvar:`MPD_TIMEOUT` env. var.)
CONNECTION_TIMEOUT = 30
#: Socket timeout in second (Default is None for no timeout)
SOCKET_TIMEOUT = None
class MPDError(Exception):
- pass
+ """Main musicpd Exception"""
class ConnectionError(MPDError):
- pass
+ """Fatal Connection Error, cannot recover from it."""
class ProtocolError(MPDError):
- pass
+ """Fatal Protocol Error, cannot recover from it"""
class CommandError(MPDError):
- pass
+ """Malformed command, socket should be fine, can reuse it"""
class CommandListError(MPDError):
- pass
+ """"""
class PendingCommandError(MPDError):
- pass
+ """"""
class IteratingError(MPDError):
- pass
+ """"""
class Range:
class MPDClient:
- """MPDClient instance will look for ``MPD_HOST``/``MPD_PORT``/``XDG_RUNTIME_DIR`` environment
- variables and set instance attribute ``host``, ``port`` and ``pwd``
- accordingly. Regarding ``MPD_HOST`` format to expose password refer
- MPD client manual :manpage:`mpc (1)`.
+ """MPDClient instance will look for :envvar:`MPD_HOST`/:envvar:`MPD_PORT`/:envvar:`XDG_RUNTIME_DIR` environment
+ variables and set instance attribute :py:attr:`host`, :py:attr:`port` and :py:obj:`pwd`
+ accordingly.
- Then :py:obj:`musicpd.MPDClient.connect` will use ``host`` and ``port`` as defaults if not provided as args.
+ Then :py:obj:`musicpd.MPDClient.connect` will use :py:obj:`host` and
+ :py:obj:`port` as defaults if not provided as args.
- Cf. :py:obj:`musicpd.MPDClient.connect` for details.
+ Regarding :envvar:`MPD_HOST` format to expose password refer this module
+ documentation or MPD client manual :manpage:`mpc (1)`.
>>> from os import environ
>>> environ['MPD_HOST'] = 'pass@mpdhost'
True
>>> cli.connect() # will use host/port as set in MPD_HOST/MPD_PORT
- :ivar str host: host used with the current connection
- :ivar str,int port: port used with the current connection
- :ivar str pwd: password detected in ``MPD_HOST`` environment variable
+ .. note::
- .. warning:: Instance attribute host/port/pwd
+ default host:
+ * use :envvar:`MPD_HOST` environment variable if set, extract password if present,
+ * else use :envvar:`XDG_RUNTIME_DIR` to looks for an existing file in ``${XDG_RUNTIME_DIR:-/run/}/mpd/socket``
+ * else set host to ``localhost``
- While :py:attr:`musicpd.MPDClient().host` and
- :py:attr:`musicpd.MPDClient().port` keep track of current connection
- host and port, :py:attr:`musicpd.MPDClient().pwd` is set once with
+ default port:
+ * use :envvar:`MPD_PORT` environment variable is set
+ * else use ``6600``
+
+ .. warning:: **Instance attribute host/port/pwd**
+
+ While :py:attr:`musicpd.MPDClient.host` and
+ :py:attr:`musicpd.MPDClient.port` keep track of current connection
+ host and port, :py:attr:`musicpd.MPDClient.pwd` is set once with
password extracted from environment variable.
- Calling :py:meth:`musicpd.MPDClient().password()` with a new password
- won't update :py:attr:`musicpd.MPDClient().pwd` value.
+ Calling :py:meth:`password` methode with a new password
+ won't update :py:attr:`musicpd.MPDClient.pwd` value.
- Moreover, :py:attr:`musicpd.MPDClient().pwd` is only an helper attribute
- exposing password extracted from ``MPD_HOST`` environment variable, it
+ Moreover, :py:attr:`musicpd.MPDClient.pwd` is only an helper attribute
+ exposing password extracted from :envvar:`MPD_HOST` environment variable, it
will not be used as default value for the :py:meth:`password` method
"""
def connect(self, host=None, port=None):
"""Connects the MPD server
- :param str host: hostname, IP or FQDN (defaults to `localhost` or socket, see below for details)
- :param port: port number (defaults to 6600)
+ :param str host: hostname, IP or FQDN (defaults to *localhost* or socket)
+ :param port: port number (defaults to *6600*)
:type port: str or int
- The connect method honors MPD_HOST/MPD_PORT environment variables.
+ If host/port are :py:obj:`None` the socket uses :py:attr:`host`/:py:attr:`port`
+ attributes as defaults. Cf. :py:obj:`MPDClient` for the logic behind default host/port.
- The underlying socket also honors MPD_TIMEOUT environment variable
+ The underlying socket also honors :envvar:`MPD_TIMEOUT` environment variable
and defaults to :py:obj:`musicpd.CONNECTION_TIMEOUT` (connect command only).
If you want to have a timeout for each command once you got connected,
set its value in :py:obj:`MPDClient.socket_timeout` (in second) or at
module level in :py:obj:`musicpd.SOCKET_TIMEOUT`.
-
- .. note:: Default host/port
-
- If host evaluate to :py:obj:`False`
- * use ``MPD_HOST`` environment variable if set, extract password if present,
- * else looks for an existing file in ``${XDG_RUNTIME_DIR:-/run/}/mpd/socket``
- * else set host to ``localhost``
-
- If port evaluate to :py:obj:`False`
- * if ``MPD_PORT`` environment variable is set, use it for port
- * else use ``6600``
"""
if not host:
host = self.host