Add albumart command

Add API documentation
Support 64 bit offsets
Use InputStream for all reads
This commit is contained in:
Ryan Walklin 2017-07-21 19:49:28 +10:00
parent 6f37f5752b
commit 9df8b32f10
4 changed files with 154 additions and 0 deletions

View File

@ -1573,6 +1573,42 @@ OK
<title>The music database</title>
<variablelist>
<varlistentry id="album_art">
<term>
<cmdsynopsis>
<command>albumart</command>
<arg choice="req"><replaceable>URI</replaceable></arg>
<arg choice="req"><replaceable>OFFSET</replaceable></arg>
</cmdsynopsis>
</term>
<listitem>
<para>
Searches the directory the file <varname>URI</varname>
resides in and attempts to return a chunk of an album
art image file at offset <varname>OFFSET</varname>.
</para>
<para>
Uses the filename "cover" with any of ".png, .jpg,
.tiff, .bmp".
</para>
<para>
Returns the file size and actual number
of bytes read at the requested offset, followed
by the chunk requested as raw bytes, then a
newline and the completion code.
</para>
<para>
Example:
</para>
<programlisting>albumart
size: 1024768
binary: 8192
&lt;8192 bytes&gt;
OK
</programlisting>
</listitem>
</varlistentry>
<varlistentry id="command_count">
<term>

View File

@ -84,6 +84,7 @@ static constexpr struct command commands[] = {
{ "add", PERMISSION_ADD, 1, 1, handle_add },
{ "addid", PERMISSION_ADD, 1, 2, handle_addid },
{ "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid },
{ "albumart", PERMISSION_READ, 2, 2, handle_album_art },
{ "channels", PERMISSION_READ, 0, 0, handle_channels },
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },

View File

@ -36,8 +36,11 @@
#include "fs/AllocatedPath.hxx"
#include "fs/FileInfo.hxx"
#include "fs/DirectoryReader.hxx"
#include "input/InputStream.hxx"
#include "LocateUri.hxx"
#include "TimePrint.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include <assert.h>
#include <inttypes.h> /* for PRIu64 */
@ -233,3 +236,114 @@ handle_read_comments(Client &client, Request args, Response &r)
gcc_unreachable();
}
/**
* Searches for the files listed in #artnames in the UTF8 folder
* URI #directory. This can be a local path or protocol-based
* URI that #InputStream supports. Returns the first successfully
* opened file or #nullptr on failure.
*/
static InputStreamPtr
find_stream_art(const char *directory, Mutex &mutex, Cond &cond)
{
static constexpr char const * art_names[] = {
"cover.png",
"cover.jpg",
"cover.tiff",
"cover.bmp"
};
for(const auto name: art_names) {
std::string art_file = PathTraitsUTF8::Build(directory, name);
try {
return InputStream::OpenReady(art_file.c_str(), mutex, cond);
} catch (const std::exception &e) {}
}
return nullptr;
}
static CommandResult
read_stream_art(Response &r, const char *uri, size_t offset)
{
const char *art_directory = PathTraitsUTF8::GetParent(uri).c_str();
Mutex mutex;
Cond cond;
InputStreamPtr is = find_stream_art(art_directory, mutex, cond);
if (is == nullptr) {
r.Error(ACK_ERROR_NO_EXIST, "No file exists");
return CommandResult::ERROR;
}
if (!is->KnownSize()) {
r.Error(ACK_ERROR_NO_EXIST, "Cannot get size for stream");
return CommandResult::ERROR;
}
const size_t art_file_size = is->GetSize();
constexpr size_t CHUNK_SIZE = 8192;
uint8_t buffer[CHUNK_SIZE];
size_t read_size;
is->Seek(offset);
read_size = is->Read(&buffer, CHUNK_SIZE);
r.Format("size: %" PRIu64 "\n"
"binary: %u\n",
art_file_size,
read_size
);
r.Write(buffer, read_size);
r.Write("\n");
return CommandResult::OK;
}
#ifdef ENABLE_DATABASE
static CommandResult
read_db_art(Client &client, Response &r, const char *uri, const uint64_t offset)
{
const Storage *storage = client.GetStorage();
if (storage == nullptr) {
r.Error(ACK_ERROR_NO_EXIST, "No database");
return CommandResult::ERROR;
}
std::string uri2 = storage->MapUTF8(uri);
return read_stream_art(r, uri2.c_str(), offset);
}
#endif
CommandResult
handle_album_art(Client &client, Request args, Response &r)
{
assert(args.size == 2);
const char *uri = args.front();
size_t offset = args.ParseUnsigned(1);
const auto located_uri = LocateUri(uri, &client
#ifdef ENABLE_DATABASE
, nullptr
#endif
);
switch (located_uri.type) {
case LocatedUri::Type::ABSOLUTE:
case LocatedUri::Type::PATH:
return read_stream_art(r, located_uri.canonical_uri, offset);
case LocatedUri::Type::RELATIVE:
#ifdef ENABLE_DATABASE
return read_db_art(client, r, located_uri.canonical_uri, offset);
#else
r.Error(ACK_ERROR_NO_EXIST, "Database disabled");
return CommandResult::ERROR;
#endif
}
r.Error(ACK_ERROR_NO_EXIST, "No art file exists");
return CommandResult::ERROR;
}

View File

@ -33,4 +33,7 @@ handle_listfiles_local(Response &response, Path path_fs);
CommandResult
handle_read_comments(Client &client, Request request, Response &response);
CommandResult
handle_album_art(Client &client, Request request, Response &response);
#endif