command/file: add command "readpicture"

Closes https://github.com/MusicPlayerDaemon/MPD/issues/42
This commit is contained in:
Max Kellermann
2019-08-12 14:01:14 +02:00
parent 54daa85ac2
commit e2da13b0d3
5 changed files with 89 additions and 0 deletions

1
NEWS

@ -2,6 +2,7 @@ ver 0.22 (not yet released)
* protocol * protocol
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and - "findadd"/"searchadd"/"searchaddpl" support the "sort" and
"window" parameters "window" parameters
- add command "readpicture" to download embedded pictures
* tags * tags
- new tags "Grouping" (for ID3 "TIT1") and "Work" - new tags "Grouping" (for ID3 "TIT1") and "Work"
* input * input

@ -1005,6 +1005,30 @@ The music database
decoder plugins support it. For example, on Ogg files, decoder plugins support it. For example, on Ogg files,
this lists the Vorbis comments. this lists the Vorbis comments.
:command:`readpicture {URI} {OFFSET}`
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).
Returns the following values:
- ``size``: the total file size
- ``type``: the file's MIME type (optional)
- ``binary``: see :ref:`binary`
If the song file was recognized, but there is no picture, the
response is successful, but is otherwise empty.
Example::
readpicture foo/bar.ogg 0
size: 1024768
type: image/jpeg
binary: 8192
<8192 bytes>
OK
.. _command_search: .. _command_search:
:command:`search {FILTER} [sort {TYPE}] [window {START:END}]` :command:`search {FILTER} [sort {TYPE}] [window {START:END}]`

@ -168,6 +168,7 @@ static constexpr struct command commands[] = {
{ "rangeid", PERMISSION_ADD, 2, 2, handle_rangeid }, { "rangeid", PERMISSION_ADD, 2, 2, handle_rangeid },
{ "readcomments", PERMISSION_READ, 1, 1, handle_read_comments }, { "readcomments", PERMISSION_READ, 1, 1, handle_read_comments },
{ "readmessages", PERMISSION_READ, 0, 0, handle_read_messages }, { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
{ "readpicture", PERMISSION_READ, 2, 2, handle_read_picture },
{ "rename", PERMISSION_CONTROL, 2, 2, handle_rename }, { "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
{ "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat }, { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
{ "replay_gain_mode", PERMISSION_CONTROL, 1, 1, { "replay_gain_mode", PERMISSION_CONTROL, 1, 1,

@ -26,6 +26,7 @@
#include "client/Client.hxx" #include "client/Client.hxx"
#include "client/Response.hxx" #include "client/Response.hxx"
#include "util/CharUtil.hxx" #include "util/CharUtil.hxx"
#include "util/OffsetPointer.hxx"
#include "util/StringView.hxx" #include "util/StringView.hxx"
#include "util/UriExtract.hxx" #include "util/UriExtract.hxx"
#include "tag/Handler.hxx" #include "tag/Handler.hxx"
@ -270,3 +271,62 @@ handle_album_art(Client &client, Request args, Response &r)
return CommandResult::ERROR; return CommandResult::ERROR;
} }
class PrintPictureHandler final : public NullTagHandler {
Response &response;
const size_t offset;
bool found = false;
bool bad_offset = false;
public:
PrintPictureHandler(Response &_response, size_t _offset) noexcept
:NullTagHandler(WANT_PICTURE), response(_response),
offset(_offset) {}
void RethrowError() const {
if (bad_offset)
throw ProtocolError(ACK_ERROR_ARG, "Bad file offset");
}
void OnPicture(const char *mime_type,
ConstBuffer<void> buffer) noexcept override {
if (found)
/* only use the first picture */
return;
found = true;
if (offset > buffer.size) {
bad_offset = true;
return;
}
response.Format("size: %" PRIoffset "\n", buffer.size);
if (mime_type != nullptr)
response.Format("type: %s\n", mime_type);
buffer.size -= offset;
if (buffer.size > Response::MAX_BINARY_SIZE)
buffer.size = Response::MAX_BINARY_SIZE;
buffer.data = OffsetPointer(buffer.data, offset);
response.WriteBinary(buffer);
}
};
CommandResult
handle_read_picture(Client &client, Request args, Response &r)
{
assert(args.size == 2);
const char *const uri = args.front();
const size_t offset = args.ParseUnsigned(1);
PrintPictureHandler handler(r, offset);
TagScanAny(client, uri, handler);
handler.RethrowError();
return CommandResult::OK;
}

@ -36,4 +36,7 @@ handle_read_comments(Client &client, Request request, Response &response);
CommandResult CommandResult
handle_album_art(Client &client, Request request, Response &response); handle_album_art(Client &client, Request request, Response &response);
CommandResult
handle_read_picture(Client &client, Request request, Response &response);
#endif #endif