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
View File

@ -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

View File

@ -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}]`

View File

@ -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,

View File

@ -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;
}

View File

@ -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