From ae178c77bdc47c954fd9a4b32ffc07fe6c4a8a49 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 24 Apr 2014 10:20:24 +0200 Subject: DatabaseCommands: "list" allows grouping --- src/command/DatabaseCommands.cxx | 21 ++++- src/db/DatabasePrint.cxx | 15 ++- src/db/DatabasePrint.hxx | 4 +- src/db/Helpers.cxx | 121 ++++++++++++++++++++++--- src/db/Helpers.hxx | 6 +- src/db/Interface.hxx | 5 +- src/db/Visitor.hxx | 3 +- src/db/plugins/LazyDatabase.cxx | 7 +- src/db/plugins/LazyDatabase.hxx | 4 +- src/db/plugins/ProxyDatabasePlugin.cxx | 13 ++- src/db/plugins/simple/SimpleDatabasePlugin.cxx | 7 +- src/db/plugins/simple/SimpleDatabasePlugin.hxx | 4 +- src/db/plugins/upnp/UpnpDatabasePlugin.cxx | 19 ++-- 13 files changed, 186 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index 71ae3ec8a..23ff593a4 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -32,6 +32,8 @@ #include "SongFilter.hxx" #include "protocol/Result.hxx" +#include + CommandResult handle_listfiles_db(Client &client, const char *uri) { @@ -191,6 +193,7 @@ handle_list(Client &client, int argc, char *argv[]) } SongFilter *filter = nullptr; + uint32_t group_mask = 0; if (args.size == 1) { /* for compatibility with < 0.12.0 */ @@ -204,6 +207,22 @@ handle_list(Client &client, int argc, char *argv[]) filter = new SongFilter((unsigned)TAG_ARTIST, args.shift()); } + while (args.size >= 2 && + strcmp(args[args.size - 2], "group") == 0) { + const char *s = args[args.size - 1]; + TagType gt = tag_name_parse_i(s); + if (gt == TAG_NUM_OF_ITEM_TYPES) { + command_error(client, ACK_ERROR_ARG, + "Unknown tag type: %s", s); + return CommandResult::ERROR; + } + + group_mask |= 1u << unsigned(gt); + + args.pop_back(); + args.pop_back(); + } + if (!args.IsEmpty()) { filter = new SongFilter(); if (!filter->Parse(args, false)) { @@ -216,7 +235,7 @@ handle_list(Client &client, int argc, char *argv[]) Error error; CommandResult ret = - listAllUniqueTags(client, tagType, filter, error) + listAllUniqueTags(client, tagType, group_mask, filter, error) ? CommandResult::OK : print_error(client, error); diff --git a/src/db/DatabasePrint.cxx b/src/db/DatabasePrint.cxx index 781866272..d84c26590 100644 --- a/src/db/DatabasePrint.cxx +++ b/src/db/DatabasePrint.cxx @@ -238,14 +238,24 @@ PrintSongURIVisitor(Client &client, const LightSong &song) static bool PrintUniqueTag(Client &client, TagType tag_type, - const char *value) + const Tag &tag) { + const char *value = tag.GetValue(tag_type); + assert(value != nullptr); client_printf(client, "%s: %s\n", tag_item_names[tag_type], value); + + for (unsigned i = 0, n = tag.num_items; i < n; i++) { + const TagItem &item = *tag.items[i]; + if (item.type != tag_type) + client_printf(client, "%s: %s\n", + tag_item_names[item.type], item.value); + } + return true; } bool -listAllUniqueTags(Client &client, unsigned type, +listAllUniqueTags(Client &client, unsigned type, uint32_t group_mask, const SongFilter *filter, Error &error) { @@ -267,6 +277,7 @@ listAllUniqueTags(Client &client, unsigned type, const auto f = std::bind(PrintUniqueTag, std::ref(client), (TagType)type, _1); return db->VisitUniqueTags(selection, (TagType)type, + group_mask, f, error); } } diff --git a/src/db/DatabasePrint.hxx b/src/db/DatabasePrint.hxx index da8fecece..c4c719ca0 100644 --- a/src/db/DatabasePrint.hxx +++ b/src/db/DatabasePrint.hxx @@ -22,6 +22,8 @@ #include "Compiler.h" +#include + class SongFilter; struct DatabaseSelection; class Client; @@ -51,7 +53,7 @@ searchStatsForSongsIn(Client &client, const char *name, Error &error); bool -listAllUniqueTags(Client &client, unsigned type, +listAllUniqueTags(Client &client, unsigned type, uint32_t group_mask, const SongFilter *filter, Error &error); diff --git a/src/db/Helpers.cxx b/src/db/Helpers.cxx index 43b0e8159..6259d4f40 100644 --- a/src/db/Helpers.cxx +++ b/src/db/Helpers.cxx @@ -22,6 +22,7 @@ #include "Interface.hxx" #include "LightSong.hxx" #include "tag/Tag.hxx" +#include "tag/TagBuilder.hxx" #include #include @@ -38,13 +39,100 @@ struct StringLess { typedef std::set StringSet; +struct TagLess { + gcc_pure + bool operator()(const Tag &a, const Tag &b) const { + if (a.num_items != b.num_items) + return a.num_items < b.num_items; + + const unsigned n = a.num_items; + for (unsigned i = 0; i < n; ++i) { + const TagItem &ai = *a.items[i]; + const TagItem &bi = *b.items[i]; + if (ai.type != bi.type) + return unsigned(ai.type) < unsigned(bi.type); + + const int cmp = strcmp(ai.value, bi.value); + if (cmp != 0) + return cmp < 0; + } + + return false; + } +}; + +typedef std::set TagSet; + +/** + * Copy all tag items of the specified type. + */ static bool -CheckUniqueTag(StringSet &set, const Tag &tag, TagType type) +CopyTagItem(TagBuilder &dest, TagType dest_type, + const Tag &src, TagType src_type) +{ + bool found = false; + const unsigned n = src.num_items; + for (unsigned i = 0; i < n; ++i) { + if (src.items[i]->type == src_type) { + dest.AddItem(dest_type, src.items[i]->value); + found = true; + } + } + + return found; +} + +/** + * Copy all tag items of the specified type. Fall back to "Artist" if + * there is no "AlbumArtist". + */ +static void +CopyTagItem(TagBuilder &dest, const Tag &src, TagType type) +{ + if (!CopyTagItem(dest, type, src, type) && + type == TAG_ALBUM_ARTIST) + CopyTagItem(dest, type, src, TAG_ARTIST); +} + +/** + * Copy all tag items of the types in the mask. + */ +static void +CopyTagMask(TagBuilder &dest, const Tag &src, uint32_t mask) +{ + for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) + if ((mask & (1u << i)) != 0) + CopyTagItem(dest, src, TagType(i)); +} + +static void +InsertUniqueTag(TagSet &set, const Tag &src, TagType type, const char *value, + uint32_t group_mask) +{ + TagBuilder builder; + if (value == nullptr) + builder.AddEmptyItem(type); + else + builder.AddItem(type, value); + CopyTagMask(builder, src, group_mask); +#if defined(__clang__) || GCC_CHECK_VERSION(4,8) + set.emplace(builder.Commit()); +#else + set.insert(builder.Commit()); +#endif +} + +static bool +CheckUniqueTag(TagSet &set, TagType dest_type, + const Tag &tag, TagType src_type, + uint32_t group_mask) { bool found = false; for (unsigned i = 0; i < tag.num_items; ++i) { - if (tag.items[i]->type == type) { - set.insert(tag.items[i]->value); + if (tag.items[i]->type == src_type) { + InsertUniqueTag(set, tag, dest_type, + tag.items[i]->value, + group_mask); found = true; } } @@ -53,35 +141,42 @@ CheckUniqueTag(StringSet &set, const Tag &tag, TagType type) } static bool -CollectTags(StringSet &set, TagType tag_type, const LightSong &song) +CollectTags(TagSet &set, TagType tag_type, uint32_t group_mask, + const LightSong &song) { + static_assert(sizeof(group_mask) * 8 >= TAG_NUM_OF_ITEM_TYPES, + "Mask is too small"); + + assert((group_mask & (1u << unsigned(tag_type))) == 0); + assert(song.tag != nullptr); const Tag &tag = *song.tag; - if (!CheckUniqueTag(set, tag, tag_type) && + if (!CheckUniqueTag(set, tag_type, tag, tag_type, group_mask) && (tag_type != TAG_ALBUM_ARTIST || /* fall back to "Artist" if no "AlbumArtist" was found */ - !CheckUniqueTag(set, tag, TAG_ARTIST))) - set.insert(""); + !CheckUniqueTag(set, tag_type, tag, TAG_ARTIST, group_mask))) + InsertUniqueTag(set, tag, tag_type, nullptr, group_mask); return true; } bool VisitUniqueTags(const Database &db, const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) { - StringSet set; + TagSet set; using namespace std::placeholders; - const auto f = std::bind(CollectTags, std::ref(set), tag_type, _1); + const auto f = std::bind(CollectTags, std::ref(set), + tag_type, group_mask, _1); if (!db.Visit(selection, f, error)) return false; - for (auto value : set) - if (!visit_string(value, error)) + for (const auto &value : set) + if (!visit_tag(value, error)) return false; return true; diff --git a/src/db/Helpers.hxx b/src/db/Helpers.hxx index 24db260c0..b9a7af4cf 100644 --- a/src/db/Helpers.hxx +++ b/src/db/Helpers.hxx @@ -23,6 +23,8 @@ #include "Visitor.hxx" #include "tag/TagType.h" +#include + class Error; class Database; struct DatabaseSelection; @@ -30,8 +32,8 @@ struct DatabaseStats; bool VisitUniqueTags(const Database &db, const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error); bool diff --git a/src/db/Interface.hxx b/src/db/Interface.hxx index 11e2da12e..5fc9265e2 100644 --- a/src/db/Interface.hxx +++ b/src/db/Interface.hxx @@ -25,6 +25,7 @@ #include "Compiler.h" #include +#include struct DatabasePlugin; struct DatabaseStats; @@ -106,8 +107,8 @@ public: * Visit all unique tag values. */ virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const = 0; virtual bool GetStats(const DatabaseSelection &selection, diff --git a/src/db/Visitor.hxx b/src/db/Visitor.hxx index 0ec29bf49..c524f1722 100644 --- a/src/db/Visitor.hxx +++ b/src/db/Visitor.hxx @@ -25,6 +25,7 @@ struct LightDirectory; struct LightSong; struct PlaylistInfo; +struct Tag; class Error; typedef std::function VisitDirectory; @@ -32,6 +33,6 @@ typedef std::function VisitSong; typedef std::function VisitPlaylist; -typedef std::function VisitString; +typedef std::function VisitTag; #endif diff --git a/src/db/plugins/LazyDatabase.cxx b/src/db/plugins/LazyDatabase.cxx index 2b576b1ee..bc52395c5 100644 --- a/src/db/plugins/LazyDatabase.cxx +++ b/src/db/plugins/LazyDatabase.cxx @@ -85,12 +85,13 @@ LazyDatabase::Visit(const DatabaseSelection &selection, bool LazyDatabase::VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const { return EnsureOpen(error) && - db->VisitUniqueTags(selection, tag_type, visit_string, error); + db->VisitUniqueTags(selection, tag_type, group_mask, visit_tag, + error); } bool diff --git a/src/db/plugins/LazyDatabase.hxx b/src/db/plugins/LazyDatabase.hxx index 000b63a98..ae1b961d0 100644 --- a/src/db/plugins/LazyDatabase.hxx +++ b/src/db/plugins/LazyDatabase.hxx @@ -52,8 +52,8 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const override; virtual bool GetStats(const DatabaseSelection &selection, diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx index 0ab25005a..a3cc640cf 100644 --- a/src/db/plugins/ProxyDatabasePlugin.cxx +++ b/src/db/plugins/ProxyDatabasePlugin.cxx @@ -112,8 +112,8 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const override; virtual bool GetStats(const DatabaseSelection &selection, @@ -715,7 +715,8 @@ ProxyDatabase::Visit(const DatabaseSelection &selection, bool ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, TagType tag_type, - VisitString visit_string, + gcc_unused uint32_t group_mask, + VisitTag visit_tag, Error &error) const { // TODO: eliminate the const_cast @@ -734,6 +735,8 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, if (!SendConstraints(connection, selection)) return CheckError(connection, error); + // TODO: use group_mask + if (!mpd_search_commit(connection)) return CheckError(connection, error); @@ -742,7 +745,9 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, struct mpd_pair *pair; while (result && (pair = mpd_recv_pair_tag(connection, tag_type2)) != nullptr) { - result = visit_string(pair->value, error); + TagBuilder tag; + tag.AddItem(tag_type, pair->value); + result = visit_tag(tag.Commit(), error); mpd_return_pair(connection, pair); } diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx index 68101f564..d616c793f 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx @@ -334,11 +334,12 @@ SimpleDatabase::Visit(const DatabaseSelection &selection, bool SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const { - return ::VisitUniqueTags(*this, selection, tag_type, visit_string, + return ::VisitUniqueTags(*this, selection, tag_type, group_mask, + visit_tag, error); } diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.hxx b/src/db/plugins/simple/SimpleDatabasePlugin.hxx index a03969f92..e27b3d956 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.hxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.hxx @@ -116,8 +116,8 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const override; virtual bool GetStats(const DatabaseSelection &selection, diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx index abc7638f8..66951f402 100644 --- a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx +++ b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx @@ -94,8 +94,8 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, - VisitString visit_string, + TagType tag_type, uint32_t group_mask, + VisitTag visit_tag, Error &error) const override; virtual bool GetStats(const DatabaseSelection &selection, @@ -721,11 +721,13 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, bool UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, - TagType tag, - VisitString visit_string, + TagType tag, gcc_unused uint32_t group_mask, + VisitTag visit_tag, Error &error) const { - if (!visit_string) + // TODO: use group_mask + + if (!visit_tag) return true; std::vector servers; @@ -754,9 +756,12 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, } } - for (const auto& value : values) - if (!visit_string(value.c_str(), error)) + for (const auto& value : values) { + TagBuilder builder; + builder.AddItem(tag, value.c_str()); + if (!visit_tag(builder.Commit(), error)) return false; + } return true; } -- cgit v1.2.3