command/file: add command "readpicture"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/42
This commit is contained in:
parent
54daa85ac2
commit
e2da13b0d3
1
NEWS
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
|
||||||
|
|
Loading…
Reference in New Issue