aboutsummaryrefslogtreecommitdiffstats
path: root/src/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/db')
-rw-r--r--src/db/ProxyDatabasePlugin.cxx482
-rw-r--r--src/db/ProxyDatabasePlugin.hxx (renamed from src/db/simple_db_plugin.h)25
-rw-r--r--src/db/SimpleDatabasePlugin.cxx (renamed from src/db/simple_db_plugin.c)280
-rw-r--r--src/db/SimpleDatabasePlugin.hxx99
4 files changed, 724 insertions, 162 deletions
diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx
new file mode 100644
index 000000000..d36cebcfd
--- /dev/null
+++ b/src/db/ProxyDatabasePlugin.cxx
@@ -0,0 +1,482 @@
+/*
+ * 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ProxyDatabasePlugin.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "gcc.h"
+
+extern "C" {
+#include "db_error.h"
+#include "conf.h"
+#include "song.h"
+#include "tag.h"
+}
+
+#include "directory.h"
+#include "playlist_vector.h"
+
+#undef MPD_DIRECTORY_H
+#undef MPD_SONG_H
+#include <mpd/client.h>
+
+#include <cassert>
+#include <string>
+#include <list>
+
+class ProxyDatabase : public Database {
+ std::string host;
+ unsigned port;
+
+ struct mpd_connection *connection;
+ struct directory *root;
+
+public:
+ static Database *Create(const struct config_param *param,
+ GError **error_r);
+
+ 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 void ReturnSong(struct song *song) const;
+
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const override;
+
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ 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);
+};
+
+G_GNUC_CONST
+static inline GQuark
+libmpdclient_quark(void)
+{
+ return g_quark_from_static_string("libmpdclient");
+}
+
+static constexpr struct {
+ enum tag_type d;
+ enum mpd_tag_type s;
+} tag_table[] = {
+ { TAG_ARTIST, MPD_TAG_ARTIST },
+ { TAG_ALBUM, MPD_TAG_ALBUM },
+ { TAG_ALBUM_ARTIST, MPD_TAG_ALBUM_ARTIST },
+ { TAG_TITLE, MPD_TAG_TITLE },
+ { TAG_TRACK, MPD_TAG_TRACK },
+ { TAG_NAME, MPD_TAG_NAME },
+ { TAG_GENRE, MPD_TAG_GENRE },
+ { TAG_DATE, MPD_TAG_DATE },
+ { TAG_COMPOSER, MPD_TAG_COMPOSER },
+ { TAG_PERFORMER, MPD_TAG_PERFORMER },
+ { TAG_COMMENT, MPD_TAG_COMMENT },
+ { TAG_DISC, MPD_TAG_DISC },
+ { TAG_MUSICBRAINZ_ARTISTID, MPD_TAG_MUSICBRAINZ_ARTISTID },
+ { TAG_MUSICBRAINZ_ALBUMID, MPD_TAG_MUSICBRAINZ_ALBUMID },
+ { TAG_MUSICBRAINZ_ALBUMARTISTID,
+ MPD_TAG_MUSICBRAINZ_ALBUMARTISTID },
+ { TAG_MUSICBRAINZ_TRACKID, MPD_TAG_MUSICBRAINZ_TRACKID },
+ { TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT }
+};
+
+G_GNUC_CONST
+static enum mpd_tag_type
+Convert(enum tag_type tag_type)
+{
+ for (auto i = tag_table; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
+ if (i->d == tag_type)
+ return i->s;
+
+ return MPD_TAG_COUNT;
+}
+
+static bool
+CheckError(struct mpd_connection *connection, GError **error_r)
+{
+ const auto error = mpd_connection_get_error(connection);
+ if (error == MPD_ERROR_SUCCESS)
+ return true;
+
+ g_set_error_literal(error_r, libmpdclient_quark(), (int)error,
+ mpd_connection_get_error_message(connection));
+ mpd_connection_clear_error(connection);
+ return false;
+}
+
+Database *
+ProxyDatabase::Create(const struct config_param *param, GError **error_r)
+{
+ ProxyDatabase *db = new ProxyDatabase();
+ if (!db->Configure(param, error_r)) {
+ delete db;
+ db = NULL;
+ }
+
+ return db;
+}
+
+bool
+ProxyDatabase::Configure(const struct config_param *param, GError **)
+{
+ host = config_get_block_string(param, "host", "");
+ port = config_get_block_unsigned(param, "port", 0);
+
+ return true;
+}
+
+bool
+ProxyDatabase::Open(GError **error_r)
+{
+ connection = mpd_connection_new(host.empty() ? NULL : host.c_str(),
+ port, 0);
+ if (connection == NULL) {
+ g_set_error_literal(error_r, libmpdclient_quark(),
+ (int)MPD_ERROR_OOM, "Out of memory");
+ return false;
+ }
+
+ if (!CheckError(connection, error_r)) {
+ mpd_connection_free(connection);
+ return false;
+ }
+
+ root = directory_new_root();
+
+ return true;
+}
+
+void
+ProxyDatabase::Close()
+{
+ assert(connection != nullptr);
+
+ directory_free(root);
+ mpd_connection_free(connection);
+}
+
+static song *
+Convert(const struct mpd_song *song);
+
+struct song *
+ProxyDatabase::GetSong(const char *uri, GError **error_r) const
+{
+ // TODO: implement
+ // TODO: auto-reconnect
+
+ if (!mpd_send_list_meta(connection, uri)) {
+ CheckError(connection, error_r);
+ return nullptr;
+ }
+
+ struct mpd_song *song = mpd_recv_song(connection);
+ struct song *song2 = song != nullptr
+ ? Convert(song)
+ : nullptr;
+ mpd_song_free(song);
+ if (!mpd_response_finish(connection)) {
+ if (song2 != nullptr)
+ song_free(song2);
+
+ CheckError(connection, error_r);
+ return nullptr;
+ }
+
+ if (song2 == nullptr)
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such song: %s", uri);
+
+ return song2;
+}
+
+void
+ProxyDatabase::ReturnSong(struct song *song) const
+{
+ assert(song != nullptr);
+ assert(song_in_database(song));
+ assert(song_is_detached(song));
+
+ song_free(song);
+}
+
+static bool
+Visit(struct mpd_connection *connection, const char *uri,
+ bool recursive, VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r);
+
+static bool
+Visit(struct mpd_connection *connection,
+ bool recursive, const struct mpd_directory *directory,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ const char *path = mpd_directory_get_path(directory);
+
+ if (visit_directory) {
+ struct directory *d =
+ directory_new(path, &detached_root);
+ bool success = visit_directory(*d, error_r);
+ directory_free(d);
+ if (!success)
+ return false;
+ }
+
+ if (recursive &&
+ !Visit(connection, path, recursive,
+ visit_directory, visit_song, visit_playlist, error_r))
+ return false;
+
+ return true;
+}
+
+static void
+Copy(struct tag *tag, enum tag_type d_tag,
+ const struct mpd_song *song, enum mpd_tag_type s_tag)
+{
+
+ for (unsigned i = 0;; ++i) {
+ const char *value = mpd_song_get_tag(song, s_tag, i);
+ if (value == NULL)
+ break;
+
+ tag_add_item(tag, d_tag, value);
+ }
+}
+
+static song *
+Convert(const struct mpd_song *song)
+{
+ struct song *s = song_detached_new(mpd_song_get_uri(song));
+
+ s->mtime = mpd_song_get_last_modified(song);
+ s->start_ms = mpd_song_get_start(song) * 1000;
+ s->end_ms = mpd_song_get_end(song) * 1000;
+
+ struct tag *tag = tag_new();
+ tag->time = mpd_song_get_duration(song);
+
+ tag_begin_add(tag);
+ for (auto i = tag_table; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
+ Copy(tag, i->d, song, i->s);
+ tag_end_add(tag);
+
+ s->tag = tag;
+
+ return s;
+}
+
+static bool
+Visit(const struct mpd_song *song,
+ VisitSong visit_song, GError **error_r)
+{
+ if (!visit_song)
+ return true;
+
+ struct song *s = Convert(song);
+ bool success = visit_song(*s, error_r);
+ song_free(s);
+
+ return success;
+}
+
+static bool
+Visit(const struct mpd_playlist *playlist,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ if (!visit_playlist)
+ return true;
+
+ struct playlist_metadata p;
+ p.name = g_strdup(mpd_playlist_get_path(playlist));
+ p.mtime = mpd_playlist_get_last_modified(playlist);
+
+ bool success = visit_playlist(p, detached_root, error_r);
+ g_free(p.name);
+
+ return success;
+}
+
+class ProxyEntity {
+ struct mpd_entity *entity;
+
+public:
+ explicit ProxyEntity(struct mpd_entity *_entity)
+ :entity(_entity) {}
+
+ ProxyEntity(const ProxyEntity &other) = delete;
+
+ ProxyEntity(ProxyEntity &&other)
+ :entity(other.entity) {
+ other.entity = nullptr;
+ }
+
+ ~ProxyEntity() {
+ if (entity != nullptr)
+ mpd_entity_free(entity);
+ }
+
+ ProxyEntity &operator=(const ProxyEntity &other) = delete;
+
+ operator const struct mpd_entity *() const {
+ return entity;
+ }
+};
+
+static std::list<ProxyEntity>
+ReceiveEntities(struct mpd_connection *connection)
+{
+ std::list<ProxyEntity> entities;
+ struct mpd_entity *entity;
+ while ((entity = mpd_recv_entity(connection)) != NULL)
+ entities.push_back(ProxyEntity(entity));
+
+ mpd_response_finish(connection);
+ return entities;
+}
+
+static bool
+Visit(struct mpd_connection *connection, const char *uri,
+ bool recursive, VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ if (!mpd_send_list_meta(connection, uri))
+ return CheckError(connection, error_r);
+
+ std::list<ProxyEntity> entities(ReceiveEntities(connection));
+ if (!CheckError(connection, error_r))
+ return false;
+
+ for (const auto &entity : entities) {
+ switch (mpd_entity_get_type(entity)) {
+ case MPD_ENTITY_TYPE_UNKNOWN:
+ break;
+
+ case MPD_ENTITY_TYPE_DIRECTORY:
+ if (!Visit(connection, recursive,
+ mpd_entity_get_directory(entity),
+ visit_directory, visit_song, visit_playlist,
+ error_r))
+ return false;
+ break;
+
+ case MPD_ENTITY_TYPE_SONG:
+ if (!Visit(mpd_entity_get_song(entity), visit_song,
+ error_r))
+ return false;
+ break;
+
+ case MPD_ENTITY_TYPE_PLAYLIST:
+ if (!Visit(mpd_entity_get_playlist(entity),
+ visit_playlist, error_r))
+ return false;
+ break;
+ }
+ }
+
+ return CheckError(connection, error_r);
+}
+
+bool
+ProxyDatabase::Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ // TODO: match
+ // TODO: auto-reconnect
+
+ return ::Visit(connection, selection.uri, selection.recursive,
+ visit_directory, visit_song, visit_playlist,
+ error_r);
+}
+
+bool
+ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const
+{
+ enum mpd_tag_type tag_type2 = Convert(tag_type);
+ if (tag_type2 == MPD_TAG_COUNT) {
+ g_set_error_literal(error_r, libmpdclient_quark(), 0,
+ "Unsupported tag");
+ return false;
+ }
+
+ if (!mpd_search_db_tags(connection, tag_type2))
+ return CheckError(connection, error_r);
+
+ // TODO: match
+ (void)selection;
+
+ if (!mpd_search_commit(connection))
+ return CheckError(connection, error_r);
+
+ bool result = true;
+
+ struct mpd_pair *pair;
+ while (result &&
+ (pair = mpd_recv_pair_tag(connection, tag_type2)) != nullptr) {
+ result = visit_string(pair->value, error_r);
+ mpd_return_pair(connection, pair);
+ }
+
+ return mpd_response_finish(connection) &&
+ CheckError(connection, error_r) &&
+ 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/simple_db_plugin.h b/src/db/ProxyDatabasePlugin.hxx
index 511505846..8e878baca 100644
--- a/src/db/simple_db_plugin.h
+++ b/src/db/ProxyDatabasePlugin.hxx
@@ -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
@@ -17,26 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SIMPLE_DB_PLUGIN_H
-#define MPD_SIMPLE_DB_PLUGIN_H
+#ifndef MPD_PROXY_DATABASE_PLUGIN_HXX
+#define MPD_PROXY_DATABASE_PLUGIN_HXX
-#include <glib.h>
-#include <stdbool.h>
-#include <time.h>
+struct DatabasePlugin;
-extern const struct db_plugin simple_db_plugin;
-
-struct db;
-
-G_GNUC_PURE
-struct directory *
-simple_db_get_root(struct db *db);
-
-bool
-simple_db_save(struct db *db, GError **error_r);
-
-G_GNUC_PURE
-time_t
-simple_db_get_mtime(const struct db *db);
+extern const DatabasePlugin proxy_db_plugin;
#endif
diff --git a/src/db/simple_db_plugin.c b/src/db/SimpleDatabasePlugin.cxx
index 697e8da5f..d83c1ca73 100644
--- a/src/db/simple_db_plugin.c
+++ b/src/db/SimpleDatabasePlugin.cxx
@@ -18,14 +18,18 @@
*/
#include "config.h"
-#include "simple_db_plugin.h"
-#include "db_internal.h"
+#include "SimpleDatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseHelpers.hxx"
+#include "SongFilter.hxx"
+
+extern "C" {
#include "db_error.h"
-#include "db_selection.h"
-#include "db_visitor.h"
#include "db_save.h"
#include "db_lock.h"
#include "conf.h"
+}
+
#include "directory.h"
#include <sys/types.h>
@@ -33,16 +37,6 @@
#include <unistd.h>
#include <errno.h>
-struct simple_db {
- struct db base;
-
- char *path;
-
- struct directory *root;
-
- time_t mtime;
-};
-
G_GNUC_CONST
static inline GQuark
simple_db_quark(void)
@@ -50,63 +44,50 @@ simple_db_quark(void)
return g_quark_from_static_string("simple_db");
}
-G_GNUC_PURE
-static const struct directory *
-simple_db_lookup_directory(const struct simple_db *db, const char *uri)
+Database *
+SimpleDatabase::Create(const struct config_param *param, GError **error_r)
{
- assert(db != NULL);
- assert(db->root != NULL);
- assert(uri != NULL);
+ SimpleDatabase *db = new SimpleDatabase();
+ if (!db->Configure(param, error_r)) {
+ delete db;
+ db = NULL;
+ }
- db_lock();
- struct directory *directory =
- directory_lookup_directory(db->root, uri);
- db_unlock();
- return directory;
+ return db;
}
-static struct db *
-simple_db_init(const struct config_param *param, GError **error_r)
+bool
+SimpleDatabase::Configure(const struct config_param *param, GError **error_r)
{
- struct simple_db *db = g_malloc(sizeof(*db));
- db_base_init(&db->base, &simple_db_plugin);
-
GError *error = NULL;
- db->path = config_dup_block_path(param, "path", &error);
- if (db->path == NULL) {
- g_free(db);
+
+ char *_path = config_dup_block_path(param, "path", &error);
+ if (_path == NULL) {
if (error != NULL)
g_propagate_error(error_r, error);
else
g_set_error(error_r, simple_db_quark(), 0,
"No \"path\" parameter specified");
- return NULL;
+ return false;
}
- return &db->base;
-}
+ path = _path;
+ free(_path);
-static void
-simple_db_finish(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- g_free(db->path);
- g_free(db);
+ return true;
}
-static bool
-simple_db_check(struct simple_db *db, GError **error_r)
+bool
+SimpleDatabase::Check(GError **error_r) const
{
- assert(db != NULL);
- assert(db->path != NULL);
+ assert(!path.empty());
/* Check if the file exists */
- if (access(db->path, F_OK)) {
+ if (access(path.c_str(), F_OK)) {
/* If the file doesn't exist, we can't check if we can write
* it, so we are going to try to get the directory path, and
* see if we can write a file in that */
- char *dirPath = g_path_get_dirname(db->path);
+ char *dirPath = g_path_get_dirname(path.c_str());
/* Check that the parent part of the path is a directory */
struct stat st;
@@ -115,7 +96,7 @@ simple_db_check(struct simple_db *db, GError **error_r)
g_set_error(error_r, simple_db_quark(), errno,
"Couldn't stat parent directory of db file "
"\"%s\": %s",
- db->path, g_strerror(errno));
+ path.c_str(), g_strerror(errno));
return false;
}
@@ -124,7 +105,7 @@ simple_db_check(struct simple_db *db, GError **error_r)
g_set_error(error_r, simple_db_quark(), 0,
"Couldn't create db file \"%s\" because the "
"parent path is not a directory",
- db->path);
+ path.c_str());
return false;
}
@@ -144,47 +125,46 @@ simple_db_check(struct simple_db *db, GError **error_r)
/* Path exists, now check if it's a regular file */
struct stat st;
- if (stat(db->path, &st) < 0) {
+ if (stat(path.c_str(), &st) < 0) {
g_set_error(error_r, simple_db_quark(), errno,
"Couldn't stat db file \"%s\": %s",
- db->path, g_strerror(errno));
+ path.c_str(), g_strerror(errno));
return false;
}
if (!S_ISREG(st.st_mode)) {
g_set_error(error_r, simple_db_quark(), 0,
"db file \"%s\" is not a regular file",
- db->path);
+ path.c_str());
return false;
}
/* And check that we can write to it */
- if (access(db->path, R_OK | W_OK)) {
+ if (access(path.c_str(), R_OK | W_OK)) {
g_set_error(error_r, simple_db_quark(), errno,
"Can't open db file \"%s\" for reading/writing: %s",
- db->path, g_strerror(errno));
+ path.c_str(), g_strerror(errno));
return false;
}
return true;
}
-static bool
-simple_db_load(struct simple_db *db, GError **error_r)
+bool
+SimpleDatabase::Load(GError **error_r)
{
- assert(db != NULL);
- assert(db->path != NULL);
- assert(db->root != NULL);
+ assert(!path.empty());
+ assert(root != NULL);
- FILE *fp = fopen(db->path, "r");
+ FILE *fp = fopen(path.c_str(), "r");
if (fp == NULL) {
g_set_error(error_r, simple_db_quark(), errno,
"Failed to open database file \"%s\": %s",
- db->path, g_strerror(errno));
+ path.c_str(), g_strerror(errno));
return false;
}
- if (!db_load_internal(fp, db->root, error_r)) {
+ if (!db_load_internal(fp, root, error_r)) {
fclose(fp);
return false;
}
@@ -192,141 +172,163 @@ simple_db_load(struct simple_db *db, GError **error_r)
fclose(fp);
struct stat st;
- if (stat(db->path, &st) == 0)
- db->mtime = st.st_mtime;
+ if (stat(path.c_str(), &st) == 0)
+ mtime = st.st_mtime;
return true;
}
-static bool
-simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
+bool
+SimpleDatabase::Open(GError **error_r)
{
- struct simple_db *db = (struct simple_db *)_db;
+ root = directory_new_root();
+ mtime = 0;
- db->root = directory_new_root();
- db->mtime = 0;
+#ifndef NDEBUG
+ borrowed_song_count = 0;
+#endif
GError *error = NULL;
- if (!simple_db_load(db, &error)) {
- directory_free(db->root);
+ if (!Load(&error)) {
+ directory_free(root);
g_warning("Failed to load database: %s", error->message);
g_error_free(error);
- if (!simple_db_check(db, error_r))
+ if (!Check(error_r))
return false;
- db->root = directory_new_root();
+ root = directory_new_root();
}
return true;
}
-static void
-simple_db_close(struct db *_db)
+void
+SimpleDatabase::Close()
{
- struct simple_db *db = (struct simple_db *)_db;
+ assert(root != NULL);
+ assert(borrowed_song_count == 0);
- assert(db->root != NULL);
-
- directory_free(db->root);
+ directory_free(root);
}
-static struct song *
-simple_db_get_song(struct db *_db, const char *uri, GError **error_r)
+struct song *
+SimpleDatabase::GetSong(const char *uri, GError **error_r) const
{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db->root != NULL);
+ assert(root != NULL);
db_lock();
- struct song *song = directory_lookup_song(db->root, uri);
+ struct song *song = directory_lookup_song(root, uri);
db_unlock();
if (song == NULL)
g_set_error(error_r, db_quark(), DB_NOT_FOUND,
"No such song: %s", uri);
+#ifndef NDEBUG
+ else
+ ++const_cast<unsigned &>(borrowed_song_count);
+#endif
return song;
}
-static bool
-simple_db_visit(struct db *_db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
+void
+SimpleDatabase::ReturnSong(gcc_unused struct song *song) const
+{
+ assert(song != nullptr);
+
+#ifndef NDEBUG
+ assert(borrowed_song_count > 0);
+ --const_cast<unsigned &>(borrowed_song_count);
+#endif
+}
+
+G_GNUC_PURE
+const struct directory *
+SimpleDatabase::LookupDirectory(const char *uri) const
+{
+ assert(root != NULL);
+ assert(uri != NULL);
+
+ ScopeDatabaseLock protect;
+ return directory_lookup_directory(root, uri);
+}
+
+bool
+SimpleDatabase::Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
{
- const struct simple_db *db = (const struct simple_db *)_db;
+ ScopeDatabaseLock protect;
+
const struct directory *directory =
- simple_db_lookup_directory(db, selection->uri);
+ directory_lookup_directory(root, selection.uri);
if (directory == NULL) {
- struct song *song;
- if (visitor->song != NULL &&
- (song = simple_db_get_song(_db, selection->uri, NULL)) != NULL)
- return visitor->song(song, ctx, error_r);
+ if (visit_song) {
+ struct song *song =
+ directory_lookup_song(root, selection.uri);
+ if (song != nullptr)
+ return !selection.Match(*song) ||
+ visit_song(*song, error_r);
+ }
g_set_error(error_r, db_quark(), DB_NOT_FOUND,
"No such directory");
return false;
}
- if (selection->recursive && visitor->directory != NULL &&
- !visitor->directory(directory, ctx, error_r))
+ if (selection.recursive && visit_directory &&
+ !visit_directory(*directory, error_r))
return false;
- db_lock();
- bool ret = directory_walk(directory, selection->recursive,
- visitor, ctx, error_r);
- db_unlock();
- return ret;
+ return directory->Walk(selection.recursive, selection.filter,
+ visit_directory, visit_song, visit_playlist,
+ error_r);
}
-const struct db_plugin simple_db_plugin = {
- .name = "simple",
- .init = simple_db_init,
- .finish = simple_db_finish,
- .open = simple_db_open,
- .close = simple_db_close,
- .get_song = simple_db_get_song,
- .visit = simple_db_visit,
-};
-
-struct directory *
-simple_db_get_root(struct db *_db)
+bool
+SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const
{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db != NULL);
- assert(db->root != NULL);
-
- return db->root;
+ return ::VisitUniqueTags(*this, selection, tag_type, visit_string,
+ error_r);
}
bool
-simple_db_save(struct db *_db, GError **error_r)
+SimpleDatabase::GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r) const
{
- struct simple_db *db = (struct simple_db *)_db;
- struct directory *music_root = db->root;
+ return ::GetStats(*this, selection, stats, error_r);
+}
+bool
+SimpleDatabase::Save(GError **error_r)
+{
db_lock();
g_debug("removing empty directories from DB");
- directory_prune_empty(music_root);
+ directory_prune_empty(root);
g_debug("sorting DB");
- directory_sort(music_root);
+ directory_sort(root);
db_unlock();
g_debug("writing DB");
- FILE *fp = fopen(db->path, "w");
+ FILE *fp = fopen(path.c_str(), "w");
if (!fp) {
g_set_error(error_r, simple_db_quark(), errno,
"unable to write to db file \"%s\": %s",
- db->path, g_strerror(errno));
+ path.c_str(), g_strerror(errno));
return false;
}
- db_save_internal(fp, music_root);
+ db_save_internal(fp, root);
if (ferror(fp)) {
g_set_error(error_r, simple_db_quark(), errno,
@@ -339,19 +341,13 @@ simple_db_save(struct db *_db, GError **error_r)
fclose(fp);
struct stat st;
- if (stat(db->path, &st) == 0)
- db->mtime = st.st_mtime;
+ if (stat(path.c_str(), &st) == 0)
+ mtime = st.st_mtime;
return true;
}
-time_t
-simple_db_get_mtime(const struct db *_db)
-{
- const struct simple_db *db = (const struct simple_db *)_db;
-
- assert(db != NULL);
- assert(db->root != NULL);
-
- return db->mtime;
-}
+const DatabasePlugin simple_db_plugin = {
+ "simple",
+ SimpleDatabase::Create,
+};
diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx
new file mode 100644
index 000000000..2ea5c4925
--- /dev/null
+++ b/src/db/SimpleDatabasePlugin.hxx
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SIMPLE_DATABASE_PLUGIN_HXX
+#define MPD_SIMPLE_DATABASE_PLUGIN_HXX
+
+#include "DatabasePlugin.hxx"
+#include "gcc.h"
+
+#include <cassert>
+#include <string>
+
+#include <stdbool.h>
+#include <time.h>
+
+struct directory;
+
+class SimpleDatabase : public Database {
+ std::string path;
+
+ struct directory *root;
+
+ time_t mtime;
+
+#ifndef NDEBUG
+ unsigned borrowed_song_count;
+#endif
+
+public:
+ gcc_pure
+ struct directory *GetRoot() {
+ assert(root != NULL);
+
+ return root;
+ }
+
+ bool Save(GError **error_r);
+
+ gcc_pure
+ time_t GetLastModified() const {
+ return mtime;
+ }
+
+ static Database *Create(const struct config_param *param,
+ GError **error_r);
+
+ 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 void ReturnSong(struct song *song) const;
+
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const override;
+
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ 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);
+
+ gcc_pure
+ bool Check(GError **error_r) const;
+
+ bool Load(GError **error_r);
+
+ gcc_pure
+ const struct directory *LookupDirectory(const char *uri) const;
+};
+
+extern const DatabasePlugin simple_db_plugin;
+
+#endif