diff options
author | Max Kellermann <max@duempel.org> | 2014-04-26 23:15:31 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-04-27 22:27:43 +0200 |
commit | bc2a1160b824c3ccf98ed658ef0d60c0f3d5bf5c (patch) | |
tree | 6daa38432619e8e0713504de3972af73007164df /src | |
parent | 75542e8f5d615c31282e6e8562bb1b71222af999 (diff) | |
download | mpd-bc2a1160b824c3ccf98ed658ef0d60c0f3d5bf5c.tar.gz mpd-bc2a1160b824c3ccf98ed658ef0d60c0f3d5bf5c.tar.xz mpd-bc2a1160b824c3ccf98ed658ef0d60c0f3d5bf5c.zip |
db/Count: implement grouping
Diffstat (limited to 'src')
-rw-r--r-- | src/command/DatabaseCommands.cxx | 18 | ||||
-rw-r--r-- | src/db/Count.cxx | 85 | ||||
-rw-r--r-- | src/db/Count.hxx | 4 |
3 files changed, 98 insertions, 9 deletions
diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index 0f23dc959..f41c89c45 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -153,14 +153,28 @@ handle_count(Client &client, int argc, char *argv[]) { ConstBuffer<const char *> args(argv + 1, argc - 1); + TagType group = TAG_NUM_OF_ITEM_TYPES; + if (args.size >= 2 && strcmp(args[args.size - 2], "group") == 0) { + const char *s = args[args.size - 1]; + group = tag_name_parse_i(s); + if (group == TAG_NUM_OF_ITEM_TYPES) { + command_error(client, ACK_ERROR_ARG, + "Unknown tag type: %s", s); + return CommandResult::ERROR; + } + + args.pop_back(); + args.pop_back(); + } + SongFilter filter; - if (!filter.Parse(args, false)) { + if (!args.IsEmpty() && !filter.Parse(args, false)) { command_error(client, ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } Error error; - return PrintSongCount(client, "", &filter, error) + return PrintSongCount(client, "", &filter, group, error) ? CommandResult::OK : print_error(client, error); } diff --git a/src/db/Count.cxx b/src/db/Count.cxx index 8c61e4c21..ec3eacd1f 100644 --- a/src/db/Count.cxx +++ b/src/db/Count.cxx @@ -23,8 +23,10 @@ #include "Interface.hxx" #include "client/Client.hxx" #include "LightSong.hxx" +#include "tag/Set.hxx" #include <functional> +#include <map> struct SearchStats { unsigned n_songs; @@ -34,6 +36,9 @@ struct SearchStats { :n_songs(0), total_time_s(0) {} }; +class TagCountMap : public std::map<std::string, SearchStats> { +}; + static void PrintSearchStats(Client &client, const SearchStats &stats) { @@ -43,6 +48,18 @@ PrintSearchStats(Client &client, const SearchStats &stats) stats.n_songs, stats.total_time_s); } +static void +Print(Client &client, TagType group, const TagCountMap &m) +{ + assert(unsigned(group) < TAG_NUM_OF_ITEM_TYPES); + + for (const auto &i : m) { + client_printf(client, "%s: %s\n", + tag_item_names[group], i.first.c_str()); + PrintSearchStats(client, i.second); + } +} + static bool stats_visitor_song(SearchStats &stats, const LightSong &song) { @@ -52,9 +69,45 @@ stats_visitor_song(SearchStats &stats, const LightSong &song) return true; } +static bool +CollectGroupCounts(TagCountMap &map, TagType group, const Tag &tag) +{ + bool found = false; + for (unsigned i = 0; i < tag.num_items; ++i) { + const TagItem &item = *tag.items[i]; + + if (item.type == group) { + auto r = map.insert(std::make_pair(item.value, + SearchStats())); + SearchStats &s = r.first->second; + ++s.n_songs; + if (tag.time > 0) + s.total_time_s += tag.time; + + found = true; + } + } + + return found; +} + +static bool +GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song) +{ + assert(song.tag != nullptr); + + const Tag &tag = *song.tag; + if (!CollectGroupCounts(map, group, tag) && group == TAG_ALBUM_ARTIST) + /* fall back to "Artist" if no "AlbumArtist" was found */ + CollectGroupCounts(map, TAG_ARTIST, tag); + + return true; +} + bool PrintSongCount(Client &client, const char *name, const SongFilter *filter, + TagType group, Error &error) { const Database *db = client.GetDatabase(error); @@ -63,14 +116,32 @@ PrintSongCount(Client &client, const char *name, const DatabaseSelection selection(name, true, filter); - SearchStats stats; + if (group == TAG_NUM_OF_ITEM_TYPES) { + /* no grouping */ - using namespace std::placeholders; - const auto f = std::bind(stats_visitor_song, std::ref(stats), - _1); - if (!db->Visit(selection, f, error)) - return false; + SearchStats stats; + + using namespace std::placeholders; + const auto f = std::bind(stats_visitor_song, std::ref(stats), + _1); + if (!db->Visit(selection, f, error)) + return false; + + PrintSearchStats(client, stats); + } else { + /* group by the specified tag: store counts in a + std::map */ + + TagCountMap map; + + using namespace std::placeholders; + const auto f = std::bind(GroupCountVisitor, std::ref(map), + group, _1); + if (!db->Visit(selection, f, error)) + return false; + + Print(client, group, map); + } - PrintSearchStats(client, stats); return true; } diff --git a/src/db/Count.hxx b/src/db/Count.hxx index 5c5fca1bd..d22a3210d 100644 --- a/src/db/Count.hxx +++ b/src/db/Count.hxx @@ -22,6 +22,9 @@ #include "Compiler.h" +#include <stdint.h> + +enum TagType : uint8_t; class Client; class SongFilter; class Error; @@ -30,6 +33,7 @@ gcc_nonnull(2) bool PrintSongCount(Client &client, const char *name, const SongFilter *filter, + TagType group, Error &error); #endif |