Skip to content

Commit 8d64840

Browse files
committed
Added support for binary responses and album artwork via the 'albumart' command
1 parent b6f2578 commit 8d64840

File tree

5 files changed

+99
-6
lines changed

5 files changed

+99
-6
lines changed

mopidy_mpd/dispatcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def _add_ok_filter(self, request, response, filter_chain):
155155
return response
156156

157157
def _has_error(self, response):
158-
return response and response[-1].startswith("ACK")
158+
return response and type(response[-1]) is str and response[-1].startswith("ACK")
159159

160160
# Filter: call handler
161161

mopidy_mpd/network.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,7 @@ def decode(self, line):
497497
def join_lines(self, lines):
498498
if not lines:
499499
return ""
500-
line_terminator = self.decode(self.terminator)
501-
return line_terminator.join(lines) + line_terminator
500+
return self.terminator.join(lines) + self.terminator
502501

503502
def send_lines(self, lines):
504503
"""
@@ -511,7 +510,7 @@ def send_lines(self, lines):
511510
return
512511

513512
# Remove all control characters (first 32 ASCII characters)
514-
lines = [line.translate(CONTROL_CHARS) for line in lines]
513+
lines = [self.encode(line.translate(CONTROL_CHARS)) if type(line) is str else line for line in lines]
515514

516515
data = self.join_lines(lines)
517-
self.connection.queue_send(self.encode(data))
516+
self.connection.queue_send(data)

mopidy_mpd/protocol/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def load_protocol_modules():
3030
:attr:`commands`.
3131
"""
3232
from . import ( # noqa
33+
album_art,
3334
audio_output,
3435
channels,
3536
command_list,

mopidy_mpd/protocol/album_art.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from mopidy_mpd import exceptions, protocol
2+
from urllib.request import urlopen
3+
4+
5+
def _get_art_url(self, track):
6+
images = self.core.library.get_images([track.uri]).get()
7+
if images[track.uri]:
8+
largest_image = sorted(
9+
images[track.uri], key=lambda i: i.width or 0, reverse=True
10+
)[0]
11+
return largest_image.uri
12+
13+
cover_cache = {}
14+
15+
@protocol.commands.add("albumart")
16+
def albumart(context, uri, offset):
17+
"""
18+
*musicpd.org, the music database section:*
19+
20+
``albumart {URI} {OFFSET}``
21+
22+
Locate album art for the given song and return a chunk of an album art image file at offset OFFSET.
23+
24+
This is currently implemented by searching the directory the file resides in for a file called cover.png, cover.jpg, cover.tiff or cover.bmp.
25+
26+
Returns the file size and actual number of bytes read at the requested offset, followed by the chunk requested as raw bytes (see Binary Responses), then a newline and the completion code.
27+
28+
Example::
29+
30+
albumart foo/bar.ogg 0
31+
size: 1024768
32+
binary: 8192
33+
<8192 bytes>
34+
OK
35+
36+
.. versionadded:: 0.21
37+
New in MPD protocol version 0.21
38+
"""
39+
global cover_cache
40+
41+
if not uri in cover_cache:
42+
(tlid, track) = context.core.playback.get_current_tl_track().get()
43+
art_url = _get_art_url(context, track)
44+
45+
cover_cache[uri] = urlopen(art_url).read()
46+
47+
data = cover_cache[uri]
48+
49+
total_size = len(data)
50+
chunk_size = 8192
51+
52+
offset = int(offset)
53+
size = min(chunk_size, total_size - offset)
54+
55+
if offset + size >= total_size:
56+
cover_cache.pop(uri)
57+
58+
return b"size: %d\nbinary: %d\n%b" % (
59+
total_size,
60+
size,
61+
data[offset:offset + size]
62+
)
63+
64+
#@protocol.commands.add("readpicture") # not yet implemented
65+
def readpicture(context, uri, offset):
66+
"""
67+
*musicpd.org, the music database section:*
68+
69+
``readpicture {URI} {OFFSET}``
70+
71+
Locate a picture for the given song and return a chunk of the image file at offset OFFSET. This is usually implemented by reading embedded pictures from binary tags (e.g. ID3v2's APIC tag).
72+
73+
Returns the following values:
74+
75+
* size: the total file size
76+
* type: the file's MIME type (optional)
77+
* binary: see Binary Responses
78+
79+
If the song file was recognized, but there is no picture, the response is successful, but is otherwise empty.
80+
81+
Example::
82+
83+
readpicture foo/bar.ogg 0
84+
size: 1024768
85+
type: image/jpeg
86+
binary: 8192
87+
<8192 bytes>
88+
OK
89+
90+
.. versionadded:: 0.21
91+
New in MPD protocol version 0.21
92+
"""
93+
raise exceptions.MpdNotImplemented # TODO

mopidy_mpd/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def on_line_received(self, line):
4545
logger.debug(
4646
"Response to %s: %s",
4747
self.connection,
48-
formatting.indent(self.decode(self.terminator).join(response)),
48+
formatting.indent(self.decode(self.terminator).join(str(response))),
4949
)
5050

5151
self.send_lines(response)

0 commit comments

Comments
 (0)