From 3c0dea811d498db3091dad868740c4653c22717e Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 15 Aug 2012 22:20:28 +0200 Subject: DatabasePlugin: add method GetStats() Optimize the ProxyDatabase by invoking "stats" on the peer, instead of visiting all songs. --- src/DatabaseHelpers.cxx | 56 ++++++++++++++++++++++++++++ src/DatabaseHelpers.hxx | 5 +++ src/DatabasePlugin.hxx | 32 ++++++++++++++++ src/Stats.cxx | 81 +++++++++-------------------------------- src/db/ProxyDatabasePlugin.cxx | 25 +++++++++++++ src/db/SimpleDatabasePlugin.cxx | 7 ++++ src/db/SimpleDatabasePlugin.hxx | 5 +++ 7 files changed, 148 insertions(+), 63 deletions(-) diff --git a/src/DatabaseHelpers.cxx b/src/DatabaseHelpers.cxx index 9a0931137..dc31a4bc2 100644 --- a/src/DatabaseHelpers.cxx +++ b/src/DatabaseHelpers.cxx @@ -76,3 +76,59 @@ VisitUniqueTags(const Database &db, const DatabaseSelection &selection, return true; } + +static void +StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums, + const struct tag &tag) +{ + if (tag.time > 0) + stats.total_duration += tag.time; + + for (unsigned i = 0; i < tag.num_items; ++i) { + const struct tag_item &item = *tag.items[i]; + + switch (item.type) { + case TAG_ARTIST: + artists.insert(item.value); + break; + + case TAG_ALBUM: + albums.insert(item.value); + break; + + default: + break; + } + } +} + +static bool +StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums, + song &song) +{ + ++stats.song_count; + + if (song.tag != nullptr) + StatsVisitTag(stats, artists, albums, *song.tag); + + return true; +} + +bool +GetStats(const Database &db, const DatabaseSelection &selection, + DatabaseStats &stats, GError **error_r) +{ + stats.Clear(); + + StringSet artists, albums; + using namespace std::placeholders; + const auto f = std::bind(StatsVisitSong, + std::ref(stats), std::ref(artists), + std::ref(albums), _1); + if (!db.Visit(selection, f, error_r)) + return false; + + stats.artist_count = artists.size(); + stats.album_count = albums.size(); + return true; +} diff --git a/src/DatabaseHelpers.hxx b/src/DatabaseHelpers.hxx index 83db182e2..cfcc94ac7 100644 --- a/src/DatabaseHelpers.hxx +++ b/src/DatabaseHelpers.hxx @@ -26,6 +26,7 @@ class Database; struct DatabaseSelection; +struct DatabaseStats; bool VisitUniqueTags(const Database &db, const DatabaseSelection &selection, @@ -33,4 +34,8 @@ VisitUniqueTags(const Database &db, const DatabaseSelection &selection, VisitString visit_string, GError **error_r); +bool +GetStats(const Database &db, const DatabaseSelection &selection, + DatabaseStats &stats, GError **error_r); + #endif diff --git a/src/DatabasePlugin.hxx b/src/DatabasePlugin.hxx index a6168b628..37df7f654 100644 --- a/src/DatabasePlugin.hxx +++ b/src/DatabasePlugin.hxx @@ -37,6 +37,34 @@ struct config_param; struct DatabaseSelection; struct db_visitor; +struct DatabaseStats { + /** + * Number of songs. + */ + unsigned song_count; + + /** + * Total duration of all songs (in seconds). + */ + unsigned long total_duration; + + /** + * Number of distinct artist names. + */ + unsigned artist_count; + + /** + * Number of distinct album names. + */ + unsigned album_count; + + void Clear() { + song_count = 0; + total_duration = 0; + artist_count = album_count = 0; + } +}; + class Database { public: /** @@ -94,6 +122,10 @@ public: enum tag_type tag_type, VisitString visit_string, GError **error_r) const = 0; + + virtual bool GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, + GError **error_r) const = 0; }; struct DatabasePlugin { diff --git a/src/Stats.cxx b/src/Stats.cxx index 07273bfdb..35c162ef2 100644 --- a/src/Stats.cxx +++ b/src/Stats.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2011 The Music Player Daemon Project + * Copyright (C) 2003-2012 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,11 +22,8 @@ extern "C" { #include "stats.h" #include "database.h" -#include "tag.h" -#include "song.h" #include "client.h" #include "player_control.h" -#include "strset.h" #include "client_internal.h" } @@ -34,9 +31,6 @@ extern "C" { #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" -#include -#include - struct stats stats; void stats_global_init(void) @@ -49,66 +43,27 @@ void stats_global_finish(void) g_timer_destroy(stats.timer); } -struct StringLess { - gcc_pure - bool operator()(const char *a, const char *b) const { - return strcmp(a, b) < 0; - } -}; - -typedef std::set StringSet; - -static void -visit_tag(StringSet &artists, StringSet &albums, const struct tag *tag) -{ - if (tag->time > 0) - stats.song_duration += tag->time; - - for (unsigned i = 0; i < tag->num_items; ++i) { - const struct tag_item *item = tag->items[i]; - - switch (item->type) { - case TAG_ARTIST: - artists.insert(item->value); - break; - - case TAG_ALBUM: - albums.insert(item->value); - break; - - default: - break; - } - } -} - -static bool -collect_stats_song(StringSet &artists, StringSet &albums, song &song) -{ - ++stats.song_count; - - if (song.tag != NULL) - visit_tag(artists, albums, song.tag); - - return true; -} - void stats_update(void) { - stats.song_count = 0; - stats.song_duration = 0; - stats.artist_count = 0; - - const DatabaseSelection selection("", true); + GError *error = nullptr; - StringSet artists, albums; - using namespace std::placeholders; - const auto f = std::bind(collect_stats_song, - std::ref(artists), std::ref(albums), _1); - GetDatabase()->Visit(selection, f, NULL); + DatabaseStats stats2; - stats.artist_count = artists.size(); - stats.album_count = albums.size(); + const DatabaseSelection selection("", true); + if (GetDatabase()->GetStats(selection, stats2, &error)) { + stats.song_count = stats2.song_count; + stats.song_duration = stats2.total_duration; + stats.artist_count = stats2.artist_count; + stats.album_count = stats2.album_count; + } else { + g_warning("%s", error->message); + g_error_free(error); + + stats.song_count = 0; + stats.song_duration = 0; + stats.artist_count = 0; + stats.album_count = 0; + } } int stats_print(struct client *client) diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx index f06728f80..3525e6f09 100644 --- a/src/db/ProxyDatabasePlugin.cxx +++ b/src/db/ProxyDatabasePlugin.cxx @@ -67,6 +67,10 @@ public: VisitString visit_string, GError **error_r) const override; + virtual bool GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, + GError **error_r) const override; + protected: bool Configure(const struct config_param *param, GError **error_r); }; @@ -420,6 +424,27 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, result; } +bool +ProxyDatabase::GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, GError **error_r) const +{ + // TODO: match + (void)selection; + + struct mpd_stats *stats2 = + mpd_run_stats(connection); + if (stats2 == nullptr) + return CheckError(connection, error_r); + + stats.song_count = mpd_stats_get_number_of_songs(stats2); + stats.total_duration = mpd_stats_get_db_play_time(stats2); + stats.artist_count = mpd_stats_get_number_of_artists(stats2); + stats.album_count = mpd_stats_get_number_of_albums(stats2); + mpd_stats_free(stats2); + + return true; +} + const DatabasePlugin proxy_db_plugin = { "proxy", ProxyDatabase::Create, diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx index ed166de45..c1de70d3e 100644 --- a/src/db/SimpleDatabasePlugin.cxx +++ b/src/db/SimpleDatabasePlugin.cxx @@ -280,6 +280,13 @@ SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection, error_r); } +bool +SimpleDatabase::GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, GError **error_r) const +{ + return ::GetStats(*this, selection, stats, error_r); +} + bool SimpleDatabase::Save(GError **error_r) { diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx index 0b7e838b5..7e3f5d2db 100644 --- a/src/db/SimpleDatabasePlugin.hxx +++ b/src/db/SimpleDatabasePlugin.hxx @@ -58,6 +58,7 @@ public: virtual bool Open(GError **error_r) override; virtual void Close() override; + virtual struct song *GetSong(const char *uri_utf8, GError **error_r) const override; virtual bool Visit(const DatabaseSelection &selection, @@ -71,6 +72,10 @@ public: VisitString visit_string, GError **error_r) const override; + virtual bool GetStats(const DatabaseSelection &selection, + DatabaseStats &stats, + GError **error_r) const override; + protected: bool Configure(const struct config_param *param, GError **error_r); -- cgit v1.2.3