diff --git a/NEWS b/NEWS index 060e98fb6..baea3bf76 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ ver 0.22 (not yet released) * protocol - "findadd"/"searchadd"/"searchaddpl" support the "sort" and "window" parameters + - add command "readpicture" to download embedded pictures * tags - new tags "Grouping" (for ID3 "TIT1") and "Work" * input diff --git a/doc/protocol.rst b/doc/protocol.rst index 9c580259c..72520d67e 100644 --- a/doc/protocol.rst +++ b/doc/protocol.rst @@ -1005,6 +1005,30 @@ The music database decoder plugins support it. For example, on Ogg files, 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 {FILTER} [sort {TYPE}] [window {START:END}]` diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index dde17d85c..005d6245e 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -168,6 +168,7 @@ static constexpr struct command commands[] = { { "rangeid", PERMISSION_ADD, 2, 2, handle_rangeid }, { "readcomments", PERMISSION_READ, 1, 1, handle_read_comments }, { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages }, + { "readpicture", PERMISSION_READ, 2, 2, handle_read_picture }, { "rename", PERMISSION_CONTROL, 2, 2, handle_rename }, { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat }, { "replay_gain_mode", PERMISSION_CONTROL, 1, 1, diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index 578bf3385..301b309fd 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -26,6 +26,7 @@ #include "client/Client.hxx" #include "client/Response.hxx" #include "util/CharUtil.hxx" +#include "util/OffsetPointer.hxx" #include "util/StringView.hxx" #include "util/UriExtract.hxx" #include "tag/Handler.hxx" @@ -270,3 +271,62 @@ handle_album_art(Client &client, Request args, Response &r) 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 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; +} diff --git a/src/command/FileCommands.hxx b/src/command/FileCommands.hxx index 25d7c2c77..2483452b1 100644 --- a/src/command/FileCommands.hxx +++ b/src/command/FileCommands.hxx @@ -36,4 +36,7 @@ handle_read_comments(Client &client, Request request, Response &response); CommandResult handle_album_art(Client &client, Request request, Response &response); +CommandResult +handle_read_picture(Client &client, Request request, Response &response); + #endif