Skip to content

Commit

Permalink
Add albumart command
Browse files Browse the repository at this point in the history
Add API documentation
Support 64 bit offsets
Use InputStream for all reads
  • Loading branch information
tokyovigilante committed Aug 15, 2017
1 parent 6f37f57 commit 9df8b32
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 0 deletions.
36 changes: 36 additions & 0 deletions doc/protocol.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions src/command/AllCommands.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
114 changes: 114 additions & 0 deletions src/command/FileCommands.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}

3 changes: 3 additions & 0 deletions src/command/FileCommands.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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

6 comments on commit 9df8b32

@skidoo23
Copy link
Contributor

@skidoo23 skidoo23 commented on 9df8b32 Aug 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally albumart. Kudos to tokyovigilante :)

Does it really work?
$ netcat localhost 6600
OK MPD 0.21.0
albumart "A/Acid Jesus - Acid Jesus/01. Mf 1.mp3" 1
ACK [50@0] {albumart} No file exists
But cover.jpg exists.

diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx
index ac8e805e3..6c5077ed4 100644
--- a/src/command/FileCommands.cxx
+++ b/src/command/FileCommands.cxx
@@ -266,12 +266,12 @@ find_stream_art(const char *directory, Mutex &mutex, Cond &cond)
 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);
+       InputStreamPtr is =
+               find_stream_art(PathTraitsUTF8::GetParent(uri).c_str(),
+               mutex, cond);

        if (is == nullptr) {
                r.Error(ACK_ERROR_NO_EXIST, "No file exists");

... and MPD starts to transmit weird binary data. Do I misunderstand your patch?

@tokyovigilante
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your diff suggests you're using an intermediate version of the patch, make sure you're using the latest master. Offset should be zero for the first byte. You shouldn't get a ACK [50@0] {albumart} No file exists and then binary data though, not sure what's going on. Can you show me the full output? And confirm that cover.jpg is in the same folder as your mp3? And your mpdconf if you don't mind.

@raftopoulos
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MPD from master. Debian testing x86_64. make clean.

$ netcat localhost 6600
OK MPD 0.21.0
albumart "mpd/Acid Jesus - Acid Jesus/01. Mf 1.mp3" 0
ACK [50@0] {albumart} No file exists

MPD output

client: [0] opened from 127.0.0.1:49474
client: [0] process command "albumart "mpd/Acid Jesus - Acid Jesus/01. Mf 1.mp3" 0"
client: [0] command returned 2

After my modification

$ netcat localhost 6600
OK MPD 0.21.0
albumart "mpd/Acid Jesus - Acid Jesus/01. Mf 1.mp3" 0
size: 113505
binary: 8192
Weird binary (JFIF).

MPD output

client: [1] opened from 127.0.0.1:49480
client: [1] process command "albumart "mp3/Acid Jesus - Acid Jesus/01. Mf 1.mp3" 0"
client: [1] command returned 0

mpd.conf

music_directory                 "/opt/mpd/music"
playlist_directory              "/opt/mpd/playlists"
log_file                        "/opt/mpd/var/log/mpd.log"
pid_file                        "/opt/mpd/var/lib/pid"
state_file                      "/opt/mpd/var/lib/state"
bind_to_address                 "0.0.0.0"
log_level                       "verbose"
auto_update     		"yes"
audio_output {
        type            "alsa"
        name            "My ALSA Device"
	device  	"hw:Intel,1"
        mixer_type      "software"
}
decoder {
        plugin          "gme"
}
decoder {
        plugin          "ffmpeg"
}
database {
        path            "/opt/mpd/var/lib/database"
        plugin          "simple"
        compress        "no"
}
input {
        plugin "curl"
}

@matthewleon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should NEWS be updated for this?

@MaxKellermann
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please.

@tokyovigilante
Copy link
Contributor Author

@tokyovigilante tokyovigilante commented on 9df8b32 Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raftopoulos @skidoo23 Should be fixed now sorry.

Please sign in to comment.