From 973c9872f930d73a8ddc98e4802b242aea9f0dba Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 24 Jan 2014 16:15:41 +0100 Subject: Sticker*: move to sticker/ --- src/sticker/SongSticker.cxx | 121 ++++++++ src/sticker/SongSticker.hxx | 86 ++++++ src/sticker/StickerDatabase.cxx | 604 ++++++++++++++++++++++++++++++++++++++++ src/sticker/StickerDatabase.hxx | 161 +++++++++++ src/sticker/StickerPrint.cxx | 44 +++ src/sticker/StickerPrint.hxx | 38 +++ 6 files changed, 1054 insertions(+) create mode 100644 src/sticker/SongSticker.cxx create mode 100644 src/sticker/SongSticker.hxx create mode 100644 src/sticker/StickerDatabase.cxx create mode 100644 src/sticker/StickerDatabase.hxx create mode 100644 src/sticker/StickerPrint.cxx create mode 100644 src/sticker/StickerPrint.hxx (limited to 'src/sticker') diff --git a/src/sticker/SongSticker.cxx b/src/sticker/SongSticker.cxx new file mode 100644 index 000000000..55143d278 --- /dev/null +++ b/src/sticker/SongSticker.cxx @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2003-2014 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 "SongSticker.hxx" +#include "StickerDatabase.hxx" +#include "LightSong.hxx" +#include "Song.hxx" +#include "Directory.hxx" + +#include + +#include +#include + +std::string +sticker_song_get_value(const LightSong &song, const char *name) +{ + const auto uri = song.GetURI(); + return sticker_load_value("song", uri.c_str(), name); +} + +bool +sticker_song_set_value(const LightSong &song, + const char *name, const char *value) +{ + const auto uri = song.GetURI(); + return sticker_store_value("song", uri.c_str(), name, value); +} + +bool +sticker_song_delete(const LightSong &song) +{ + const auto uri = song.GetURI(); + return sticker_delete("song", uri.c_str()); +} + +bool +sticker_song_delete_value(const LightSong &song, const char *name) +{ + const auto uri = song.GetURI(); + return sticker_delete_value("song", uri.c_str(), name); +} + +struct sticker * +sticker_song_get(const LightSong &song) +{ + const auto uri = song.GetURI(); + return sticker_load("song", uri.c_str()); +} + +struct sticker_song_find_data { + Directory *directory; + const char *base_uri; + size_t base_uri_length; + + void (*func)(const LightSong &song, const char *value, + void *user_data); + void *user_data; +}; + +static void +sticker_song_find_cb(const char *uri, const char *value, void *user_data) +{ + struct sticker_song_find_data *data = + (struct sticker_song_find_data *)user_data; + + if (memcmp(uri, data->base_uri, data->base_uri_length) != 0) + /* should not happen, ignore silently */ + return; + + Song *song = data->directory->LookupSong(uri + data->base_uri_length); + if (song != nullptr) + data->func(song->Export(), value, data->user_data); +} + +bool +sticker_song_find(Directory &directory, const char *name, + void (*func)(const LightSong &song, const char *value, + void *user_data), + void *user_data) +{ + struct sticker_song_find_data data; + data.directory = &directory; + data.func = func; + data.user_data = user_data; + + char *allocated; + data.base_uri = directory.GetPath(); + if (*data.base_uri != 0) + /* append slash to base_uri */ + data.base_uri = allocated = + g_strconcat(data.base_uri, "/", nullptr); + else + /* searching in root directory - no trailing slash */ + allocated = nullptr; + + data.base_uri_length = strlen(data.base_uri); + + bool success = sticker_find("song", data.base_uri, name, + sticker_song_find_cb, &data); + g_free(allocated); + + return success; +} diff --git a/src/sticker/SongSticker.hxx b/src/sticker/SongSticker.hxx new file mode 100644 index 000000000..2f977bd21 --- /dev/null +++ b/src/sticker/SongSticker.hxx @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003-2014 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_SONG_STICKER_HXX +#define MPD_SONG_STICKER_HXX + +#include "Compiler.h" + +#include + +struct LightSong; +struct Directory; +struct sticker; + +/** + * Returns one value from a song's sticker record. The caller must + * free the return value with g_free(). + */ +gcc_pure +std::string +sticker_song_get_value(const LightSong &song, const char *name); + +/** + * Sets a sticker value in the specified song. Overwrites existing + * values. + */ +bool +sticker_song_set_value(const LightSong &song, + const char *name, const char *value); + +/** + * Deletes a sticker from the database. All values are deleted. + */ +bool +sticker_song_delete(const LightSong &song); + +/** + * Deletes a sticker value. Does nothing if the sticker did not + * exist. + */ +bool +sticker_song_delete_value(const LightSong &song, const char *name); + +/** + * Loads the sticker for the specified song. + * + * @param song the song object + * @return a sticker object, or NULL on error or if there is no sticker + */ +sticker * +sticker_song_get(const LightSong &song); + +/** + * Finds stickers with the specified name below the specified + * directory. + * + * Caller must lock the #db_mutex. + * + * @param directory the base directory to search in + * @param name the name of the sticker + * @return true on success (even if no sticker was found), false on + * failure + */ +bool +sticker_song_find(Directory &directory, const char *name, + void (*func)(const LightSong &song, const char *value, + void *user_data), + void *user_data); + +#endif diff --git a/src/sticker/StickerDatabase.cxx b/src/sticker/StickerDatabase.cxx new file mode 100644 index 000000000..93eaa900d --- /dev/null +++ b/src/sticker/StickerDatabase.cxx @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2003-2014 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 "StickerDatabase.hxx" +#include "fs/Path.hxx" +#include "Idle.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "util/Macros.hxx" +#include "Log.hxx" + +#include +#include + +#include +#include + +#if SQLITE_VERSION_NUMBER < 3003009 +#define sqlite3_prepare_v2 sqlite3_prepare +#endif + +struct sticker { + std::map table; +}; + +enum sticker_sql { + STICKER_SQL_GET, + STICKER_SQL_LIST, + STICKER_SQL_UPDATE, + STICKER_SQL_INSERT, + STICKER_SQL_DELETE, + STICKER_SQL_DELETE_VALUE, + STICKER_SQL_FIND, +}; + +static const char *const sticker_sql[] = { + //[STICKER_SQL_GET] = + "SELECT value FROM sticker WHERE type=? AND uri=? AND name=?", + //[STICKER_SQL_LIST] = + "SELECT name,value FROM sticker WHERE type=? AND uri=?", + //[STICKER_SQL_UPDATE] = + "UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?", + //[STICKER_SQL_INSERT] = + "INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)", + //[STICKER_SQL_DELETE] = + "DELETE FROM sticker WHERE type=? AND uri=?", + //[STICKER_SQL_DELETE_VALUE] = + "DELETE FROM sticker WHERE type=? AND uri=? AND name=?", + //[STICKER_SQL_FIND] = + "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?", +}; + +static const char sticker_sql_create[] = + "CREATE TABLE IF NOT EXISTS sticker(" + " type VARCHAR NOT NULL, " + " uri VARCHAR NOT NULL, " + " name VARCHAR NOT NULL, " + " value VARCHAR NOT NULL" + ");" + "CREATE UNIQUE INDEX IF NOT EXISTS" + " sticker_value ON sticker(type, uri, name);" + ""; + +static sqlite3 *sticker_db; +static sqlite3_stmt *sticker_stmt[ARRAY_SIZE(sticker_sql)]; + +static constexpr Domain sticker_domain("sticker"); + +static void +LogError(sqlite3 *db, const char *msg) +{ + FormatError(sticker_domain, "%s: %s", msg, sqlite3_errmsg(db)); +} + +static sqlite3_stmt * +sticker_prepare(const char *sql, Error &error) +{ + int ret; + sqlite3_stmt *stmt; + + ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, nullptr); + if (ret != SQLITE_OK) { + error.Format(sticker_domain, ret, + "sqlite3_prepare_v2() failed: %s", + sqlite3_errmsg(sticker_db)); + return nullptr; + } + + return stmt; +} + +bool +sticker_global_init(Path path, Error &error) +{ + assert(!path.IsNull()); + + int ret; + + /* open/create the sqlite database */ + + ret = sqlite3_open(path.c_str(), &sticker_db); + if (ret != SQLITE_OK) { + const std::string utf8 = path.ToUTF8(); + error.Format(sticker_domain, ret, + "Failed to open sqlite database '%s': %s", + utf8.c_str(), sqlite3_errmsg(sticker_db)); + return false; + } + + /* create the table and index */ + + ret = sqlite3_exec(sticker_db, sticker_sql_create, + nullptr, nullptr, nullptr); + if (ret != SQLITE_OK) { + error.Format(sticker_domain, ret, + "Failed to create sticker table: %s", + sqlite3_errmsg(sticker_db)); + return false; + } + + /* prepare the statements we're going to use */ + + for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) { + assert(sticker_sql[i] != nullptr); + + sticker_stmt[i] = sticker_prepare(sticker_sql[i], error); + if (sticker_stmt[i] == nullptr) + return false; + } + + return true; +} + +void +sticker_global_finish(void) +{ + if (sticker_db == nullptr) + /* not configured */ + return; + + for (unsigned i = 0; i < ARRAY_SIZE(sticker_stmt); ++i) { + assert(sticker_stmt[i] != nullptr); + + sqlite3_finalize(sticker_stmt[i]); + } + + sqlite3_close(sticker_db); +} + +bool +sticker_enabled(void) +{ + return sticker_db != nullptr; +} + +std::string +sticker_load_value(const char *type, const char *uri, const char *name) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET]; + int ret; + + assert(sticker_enabled()); + assert(type != nullptr); + assert(uri != nullptr); + assert(name != nullptr); + + if (*name == 0) + return std::string(); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return std::string(); + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return std::string(); + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return std::string(); + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + std::string value; + if (ret == SQLITE_ROW) { + /* record found */ + value = (const char*)sqlite3_column_text(stmt, 0); + } else if (ret == SQLITE_DONE) { + /* no record found */ + } else { + /* error */ + LogError(sticker_db, "sqlite3_step() failed"); + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + return value; +} + +static bool +sticker_list_values(std::map &table, + const char *type, const char *uri) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST]; + int ret; + + assert(type != nullptr); + assert(uri != nullptr); + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + do { + ret = sqlite3_step(stmt); + switch (ret) { + const char *name, *value; + + case SQLITE_ROW: + name = (const char*)sqlite3_column_text(stmt, 0); + value = (const char*)sqlite3_column_text(stmt, 1); + + table.insert(std::make_pair(name, value)); + break; + case SQLITE_DONE: + break; + case SQLITE_BUSY: + /* no op */ + break; + default: + LogError(sticker_db, "sqlite3_step() failed"); + return false; + } + } while (ret != SQLITE_DONE); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + return true; +} + +static bool +sticker_update_value(const char *type, const char *uri, + const char *name, const char *value) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE]; + int ret; + + assert(type != nullptr); + assert(uri != nullptr); + assert(name != nullptr); + assert(*name != 0); + assert(value != nullptr); + + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, value, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 4, name, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + LogError(sticker_db, "sqlite3_step() failed"); + return false; + } + + ret = sqlite3_changes(sticker_db); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + idle_add(IDLE_STICKER); + return ret > 0; +} + +static bool +sticker_insert_value(const char *type, const char *uri, + const char *name, const char *value) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT]; + int ret; + + assert(type != nullptr); + assert(uri != nullptr); + assert(name != nullptr); + assert(*name != 0); + assert(value != nullptr); + + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 4, value, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + LogError(sticker_db, "sqlite3_step() failed"); + return false; + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + + idle_add(IDLE_STICKER); + return true; +} + +bool +sticker_store_value(const char *type, const char *uri, + const char *name, const char *value) +{ + assert(sticker_enabled()); + assert(type != nullptr); + assert(uri != nullptr); + assert(name != nullptr); + assert(value != nullptr); + + if (*name == 0) + return false; + + return sticker_update_value(type, uri, name, value) || + sticker_insert_value(type, uri, name, value); +} + +bool +sticker_delete(const char *type, const char *uri) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE]; + int ret; + + assert(sticker_enabled()); + assert(type != nullptr); + assert(uri != nullptr); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + LogError(sticker_db, "sqlite3_step() failed"); + return false; + } + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + idle_add(IDLE_STICKER); + return true; +} + +bool +sticker_delete_value(const char *type, const char *uri, const char *name) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE]; + int ret; + + assert(sticker_enabled()); + assert(type != nullptr); + assert(uri != nullptr); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + do { + ret = sqlite3_step(stmt); + } while (ret == SQLITE_BUSY); + + if (ret != SQLITE_DONE) { + LogError(sticker_db, "sqlite3_step() failed"); + return false; + } + + ret = sqlite3_changes(sticker_db); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + idle_add(IDLE_STICKER); + return ret > 0; +} + +void +sticker_free(struct sticker *sticker) +{ + delete sticker; +} + +const char * +sticker_get_value(const struct sticker &sticker, const char *name) +{ + auto i = sticker.table.find(name); + if (i == sticker.table.end()) + return nullptr; + + return i->second.c_str(); +} + +void +sticker_foreach(const sticker &sticker, + void (*func)(const char *name, const char *value, + void *user_data), + void *user_data) +{ + for (const auto &i : sticker.table) + func(i.first.c_str(), i.second.c_str(), user_data); +} + +struct sticker * +sticker_load(const char *type, const char *uri) +{ + sticker s; + + if (!sticker_list_values(s.table, type, uri)) + return nullptr; + + if (s.table.empty()) + /* don't return empty sticker objects */ + return nullptr; + + return new sticker(std::move(s)); +} + +bool +sticker_find(const char *type, const char *base_uri, const char *name, + void (*func)(const char *uri, const char *value, + void *user_data), + void *user_data) +{ + sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_FIND]; + int ret; + + assert(type != nullptr); + assert(name != nullptr); + assert(func != nullptr); + assert(sticker_enabled()); + + sqlite3_reset(stmt); + + ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + if (base_uri == nullptr) + base_uri = ""; + + ret = sqlite3_bind_text(stmt, 2, base_uri, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); + if (ret != SQLITE_OK) { + LogError(sticker_db, "sqlite3_bind_text() failed"); + return false; + } + + do { + ret = sqlite3_step(stmt); + switch (ret) { + case SQLITE_ROW: + func((const char*)sqlite3_column_text(stmt, 0), + (const char*)sqlite3_column_text(stmt, 1), + user_data); + break; + case SQLITE_DONE: + break; + case SQLITE_BUSY: + /* no op */ + break; + default: + LogError(sticker_db, "sqlite3_step() failed"); + return false; + } + } while (ret != SQLITE_DONE); + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + + return true; +} diff --git a/src/sticker/StickerDatabase.hxx b/src/sticker/StickerDatabase.hxx new file mode 100644 index 000000000..8993489c4 --- /dev/null +++ b/src/sticker/StickerDatabase.hxx @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2003-2014 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. + */ + +/* + * This is the sticker database library. It is the backend of all the + * sticker code in MPD. + * + * "Stickers" are pieces of information attached to existing MPD + * objects (e.g. song files, directories, albums). Clients can create + * arbitrary name/value pairs. MPD itself does not assume any special + * meaning in them. + * + * The goal is to allow clients to share additional (possibly dynamic) + * information about songs, which is neither stored on the client (not + * available to other clients), nor stored in the song files (MPD has + * no write access). + * + * Client developers should create a standard for common sticker + * names, to ensure interoperability. + * + * Examples: song ratings; statistics; deferred tag writes; lyrics; + * ... + * + */ + +#ifndef MPD_STICKER_DATABASE_HXX +#define MPD_STICKER_DATABASE_HXX + +#include "Compiler.h" + +#include + +class Error; +class Path; +struct sticker; + +/** + * Opens the sticker database. + * + * @return true on success, false on error + */ +bool +sticker_global_init(Path path, Error &error); + +/** + * Close the sticker database. + */ +void +sticker_global_finish(void); + +/** + * Returns true if the sticker database is configured and available. + */ +gcc_const +bool +sticker_enabled(void); + +/** + * Returns one value from an object's sticker record. Returns an + * empty string if the value doesn't exist. + */ +std::string +sticker_load_value(const char *type, const char *uri, const char *name); + +/** + * Sets a sticker value in the specified object. Overwrites existing + * values. + */ +bool +sticker_store_value(const char *type, const char *uri, + const char *name, const char *value); + +/** + * Deletes a sticker from the database. All sticker values of the + * specified object are deleted. + */ +bool +sticker_delete(const char *type, const char *uri); + +/** + * Deletes a sticker value. Fails if no sticker with this name + * exists. + */ +bool +sticker_delete_value(const char *type, const char *uri, const char *name); + +/** + * Frees resources held by the sticker object. + * + * @param sticker the sticker object to be freed + */ +void +sticker_free(sticker *sticker); + +/** + * Determines a single value in a sticker. + * + * @param sticker the sticker object + * @param name the name of the sticker + * @return the sticker value, or nullptr if none was found + */ +gcc_pure +const char * +sticker_get_value(const sticker &sticker, const char *name); + +/** + * Iterates over all sticker items in a sticker. + * + * @param sticker the sticker object + * @param func a callback function + * @param user_data an opaque pointer for the callback function + */ +void +sticker_foreach(const sticker &sticker, + void (*func)(const char *name, const char *value, + void *user_data), + void *user_data); + +/** + * Loads the sticker for the specified resource. + * + * @param type the resource type, e.g. "song" + * @param uri the URI of the resource, e.g. the song path + * @return a sticker object, or nullptr on error or if there is no sticker + */ +sticker * +sticker_load(const char *type, const char *uri); + +/** + * Finds stickers with the specified name below the specified URI. + * + * @param type the resource type, e.g. "song" + * @param base_uri the URI prefix of the resources, or nullptr if all + * resources should be searched + * @param name the name of the sticker + * @return true on success (even if no sticker was found), false on + * failure + */ +bool +sticker_find(const char *type, const char *base_uri, const char *name, + void (*func)(const char *uri, const char *value, + void *user_data), + void *user_data); + +#endif diff --git a/src/sticker/StickerPrint.cxx b/src/sticker/StickerPrint.cxx new file mode 100644 index 000000000..a952ff203 --- /dev/null +++ b/src/sticker/StickerPrint.cxx @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2003-2014 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 "StickerPrint.hxx" +#include "StickerDatabase.hxx" +#include "client/Client.hxx" + +void +sticker_print_value(Client &client, + const char *name, const char *value) +{ + client_printf(client, "sticker: %s=%s\n", name, value); +} + +static void +print_sticker_cb(const char *name, const char *value, void *data) +{ + Client &client = *(Client *)data; + + sticker_print_value(client, name, value); +} + +void +sticker_print(Client &client, const sticker &sticker) +{ + sticker_foreach(sticker, print_sticker_cb, &client); +} diff --git a/src/sticker/StickerPrint.hxx b/src/sticker/StickerPrint.hxx new file mode 100644 index 000000000..39f3dc09e --- /dev/null +++ b/src/sticker/StickerPrint.hxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2003-2014 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_STICKER_PRINT_HXX +#define MPD_STICKER_PRINT_HXX + +struct sticker; +class Client; + +/** + * Sends one sticker value to the client. + */ +void +sticker_print_value(Client &client, const char *name, const char *value); + +/** + * Sends all sticker values to the client. + */ +void +sticker_print(Client &client, const sticker &sticker); + +#endif -- cgit v1.2.3