|
|
|
@@ -34,6 +34,7 @@
|
|
|
|
|
#include "tag/Builder.hxx"
|
|
|
|
|
#include "tag/Tag.hxx"
|
|
|
|
|
#include "tag/Mask.hxx"
|
|
|
|
|
#include "tag/ParseName.hxx"
|
|
|
|
|
#include "util/ScopeExit.hxx"
|
|
|
|
|
#include "util/RuntimeError.hxx"
|
|
|
|
|
#include "protocol/Ack.hxx"
|
|
|
|
@@ -335,6 +336,35 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
SendGroupMask(mpd_connection *connection, TagMask mask)
|
|
|
|
|
{
|
|
|
|
|
#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
|
|
|
|
|
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
|
|
|
|
|
const auto tag_type = TagType(i);
|
|
|
|
|
if (!mask.Test(tag_type))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const auto tag = Convert(tag_type);
|
|
|
|
|
if (tag == MPD_TAG_COUNT)
|
|
|
|
|
throw std::runtime_error("Unsupported tag");
|
|
|
|
|
|
|
|
|
|
if (!mpd_search_add_group_tag(connection, tag))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
(void)connection;
|
|
|
|
|
(void)mask;
|
|
|
|
|
|
|
|
|
|
if (mask.TestAny())
|
|
|
|
|
throw std::runtime_error("Grouping requires libmpdclient 2.12");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
|
|
|
|
|
const ConfigBlock &block)
|
|
|
|
|
:Database(proxy_db_plugin),
|
|
|
|
@@ -700,7 +730,7 @@ static void
|
|
|
|
|
SearchSongs(struct mpd_connection *connection,
|
|
|
|
|
const DatabaseSelection &selection,
|
|
|
|
|
VisitSong visit_song)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
assert(selection.recursive);
|
|
|
|
|
assert(visit_song);
|
|
|
|
|
|
|
|
|
@@ -727,6 +757,11 @@ SearchSongs(struct mpd_connection *connection,
|
|
|
|
|
|
|
|
|
|
if (!mpd_response_finish(connection))
|
|
|
|
|
ThrowError(connection);
|
|
|
|
|
} catch (...) {
|
|
|
|
|
if (connection != nullptr)
|
|
|
|
|
mpd_search_cancel(connection);
|
|
|
|
|
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -774,9 +809,9 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
|
|
|
|
|
void
|
|
|
|
|
ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
|
|
|
|
TagType tag_type,
|
|
|
|
|
gcc_unused TagMask group_mask,
|
|
|
|
|
TagMask group_mask,
|
|
|
|
|
VisitTag visit_tag) const
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
// TODO: eliminate the const_cast
|
|
|
|
|
const_cast<ProxyDatabase *>(this)->EnsureConnected();
|
|
|
|
|
|
|
|
|
@@ -785,32 +820,47 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
|
|
|
|
throw std::runtime_error("Unsupported tag");
|
|
|
|
|
|
|
|
|
|
if (!mpd_search_db_tags(connection, tag_type2) ||
|
|
|
|
|
!SendConstraints(connection, selection))
|
|
|
|
|
!SendConstraints(connection, selection) ||
|
|
|
|
|
!SendGroupMask(connection, group_mask))
|
|
|
|
|
ThrowError(connection);
|
|
|
|
|
|
|
|
|
|
// TODO: use group_mask
|
|
|
|
|
|
|
|
|
|
if (!mpd_search_commit(connection))
|
|
|
|
|
ThrowError(connection);
|
|
|
|
|
|
|
|
|
|
while (auto *pair = mpd_recv_pair_tag(connection, tag_type2)) {
|
|
|
|
|
TagBuilder builder;
|
|
|
|
|
|
|
|
|
|
while (auto *pair = mpd_recv_pair(connection)) {
|
|
|
|
|
AtScopeExit(this, pair) {
|
|
|
|
|
mpd_return_pair(connection, pair);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TagBuilder tag;
|
|
|
|
|
tag.AddItem(tag_type, pair->value);
|
|
|
|
|
const auto current_type = tag_name_parse_i(pair->name);
|
|
|
|
|
if (current_type == TAG_NUM_OF_ITEM_TYPES)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (tag.empty())
|
|
|
|
|
if (current_type == tag_type && !builder.empty()) {
|
|
|
|
|
try {
|
|
|
|
|
visit_tag(builder.Commit());
|
|
|
|
|
} catch (...) {
|
|
|
|
|
mpd_response_finish(connection);
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.AddItem(current_type, pair->value);
|
|
|
|
|
|
|
|
|
|
if (!builder.HasType(current_type))
|
|
|
|
|
/* if no tag item has been added, then the
|
|
|
|
|
given value was not acceptable
|
|
|
|
|
(e.g. empty); forcefully insert an empty
|
|
|
|
|
tag in this case, as the caller expects the
|
|
|
|
|
given tag type to be present */
|
|
|
|
|
tag.AddEmptyItem(tag_type);
|
|
|
|
|
builder.AddEmptyItem(current_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!builder.empty()) {
|
|
|
|
|
try {
|
|
|
|
|
visit_tag(tag.Commit());
|
|
|
|
|
visit_tag(builder.Commit());
|
|
|
|
|
} catch (...) {
|
|
|
|
|
mpd_response_finish(connection);
|
|
|
|
|
throw;
|
|
|
|
@@ -819,6 +869,11 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
|
|
|
|
|
|
|
|
|
if (!mpd_response_finish(connection))
|
|
|
|
|
ThrowError(connection);
|
|
|
|
|
} catch (...) {
|
|
|
|
|
if (connection != nullptr)
|
|
|
|
|
mpd_search_cancel(connection);
|
|
|
|
|
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DatabaseStats
|
|
|
|
|