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