sticker: new library for storing dynamic information about songs
"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.
This commit is contained in:
parent
fbed96dcea
commit
145ab84d51
@ -43,6 +43,10 @@ The default is "yes".
|
||||
.B db_file <file>
|
||||
This specifies where the db file will be stored.
|
||||
.TP
|
||||
.B sticker_file <file>
|
||||
The location of the sticker database. This is a database which
|
||||
manages dynamic information attached to songs.
|
||||
.TP
|
||||
.B log_file <file>
|
||||
This specifies where the log file should be located.
|
||||
The special value "syslog" makes MPD use the local syslog daemon.
|
||||
|
@ -100,6 +100,7 @@ mpd_headers = \
|
||||
songvec.h \
|
||||
state_file.h \
|
||||
stats.h \
|
||||
sticker.h \
|
||||
tag.h \
|
||||
tag_internal.h \
|
||||
tag_pool.h \
|
||||
@ -192,6 +193,10 @@ mpd_SOURCES = \
|
||||
stored_playlist.c \
|
||||
timer.c
|
||||
|
||||
if ENABLE_SQLITE
|
||||
mpd_SOURCES += sticker.c
|
||||
endif
|
||||
|
||||
if HAVE_LIBSAMPLERATE
|
||||
mpd_SOURCES += pcm_resample_libsamplerate.c
|
||||
else
|
||||
|
@ -172,6 +172,7 @@ void config_global_init(void)
|
||||
registerConfigParam(CONF_FOLLOW_INSIDE_SYMLINKS, 0, 0);
|
||||
registerConfigParam(CONF_FOLLOW_OUTSIDE_SYMLINKS, 0, 0);
|
||||
registerConfigParam(CONF_DB_FILE, 0, 0);
|
||||
registerConfigParam(CONF_STICKER_FILE, false, false);
|
||||
registerConfigParam(CONF_LOG_FILE, 0, 0);
|
||||
registerConfigParam(CONF_ERROR_FILE, 0, 0);
|
||||
registerConfigParam(CONF_PID_FILE, 0, 0);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define CONF_FOLLOW_INSIDE_SYMLINKS "follow_inside_symlinks"
|
||||
#define CONF_FOLLOW_OUTSIDE_SYMLINKS "follow_outside_symlinks"
|
||||
#define CONF_DB_FILE "db_file"
|
||||
#define CONF_STICKER_FILE "sticker_file"
|
||||
#define CONF_LOG_FILE "log_file"
|
||||
#define CONF_ERROR_FILE "error_file"
|
||||
#define CONF_PID_FILE "pid_file"
|
||||
|
12
src/main.c
12
src/main.c
@ -54,6 +54,10 @@
|
||||
#include "songvec.h"
|
||||
#include "tag_pool.h"
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
#include "sticker.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
#include "archive_list.h"
|
||||
#endif
|
||||
@ -235,6 +239,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
openDB(&options, argv[0]);
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
sticker_global_init(config_get_path(CONF_STICKER_FILE));
|
||||
#endif
|
||||
|
||||
command_init();
|
||||
initialize_decoder_and_player();
|
||||
initAudioConfig();
|
||||
@ -278,6 +286,10 @@ int main(int argc, char *argv[])
|
||||
g_debug("db_finish took %f seconds",
|
||||
((float)(clock()-start))/CLOCKS_PER_SEC);
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
sticker_global_finish();
|
||||
#endif
|
||||
|
||||
notify_deinit(&main_notify);
|
||||
event_pipe_deinit();
|
||||
|
||||
|
361
src/sticker.c
Normal file
361
src/sticker.c
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "sticker.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <sqlite3.h>
|
||||
#include <assert.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "sticker"
|
||||
|
||||
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 const char sticker_sql_get[] =
|
||||
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?";
|
||||
|
||||
static const char sticker_sql_update[] =
|
||||
"UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?";
|
||||
|
||||
static const char sticker_sql_insert[] =
|
||||
"INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)";
|
||||
|
||||
static const char sticker_sql_delete[] =
|
||||
"DELETE FROM sticker WHERE type=? AND uri=?";
|
||||
|
||||
static sqlite3 *sticker_db;
|
||||
static sqlite3_stmt *sticker_stmt_get, *sticker_stmt_update,
|
||||
*sticker_stmt_insert, *sticker_stmt_delete;
|
||||
|
||||
static sqlite3_stmt *
|
||||
sticker_prepare(const char *sql)
|
||||
{
|
||||
int ret;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
g_error("sqlite3_prepare_v2() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
void
|
||||
sticker_global_init(const char *path)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (path == NULL)
|
||||
/* not configured */
|
||||
return;
|
||||
|
||||
/* open/create the sqlite database */
|
||||
|
||||
ret = sqlite3_open(path, &sticker_db);
|
||||
if (ret != SQLITE_OK)
|
||||
g_error("Failed to open sqlite database '%s': %s",
|
||||
path, sqlite3_errmsg(sticker_db));
|
||||
|
||||
/* create the table and index */
|
||||
|
||||
ret = sqlite3_exec(sticker_db, sticker_sql_create, NULL, NULL, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
g_error("Failed to create sticker table: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
|
||||
/* prepare the statements we're going to use */
|
||||
|
||||
sticker_stmt_get = sticker_prepare(sticker_sql_get);
|
||||
sticker_stmt_update = sticker_prepare(sticker_sql_update);
|
||||
sticker_stmt_insert = sticker_prepare(sticker_sql_insert);
|
||||
sticker_stmt_delete = sticker_prepare(sticker_sql_delete);
|
||||
|
||||
if (sticker_stmt_get == NULL || sticker_stmt_update == NULL ||
|
||||
sticker_stmt_insert == NULL || sticker_stmt_delete == NULL)
|
||||
g_error("Failed to prepare sqlite statements");
|
||||
}
|
||||
|
||||
void
|
||||
sticker_global_finish(void)
|
||||
{
|
||||
if (sticker_db == NULL)
|
||||
/* not configured */
|
||||
return;
|
||||
|
||||
sqlite3_finalize(sticker_stmt_delete);
|
||||
sqlite3_finalize(sticker_stmt_update);
|
||||
sqlite3_finalize(sticker_stmt_insert);
|
||||
sqlite3_close(sticker_db);
|
||||
}
|
||||
|
||||
bool
|
||||
sticker_enabled(void)
|
||||
{
|
||||
return sticker_db != NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
sticker_load_value(const char *type, const char *uri, const char *name)
|
||||
{
|
||||
int ret;
|
||||
char *value;
|
||||
|
||||
assert(sticker_enabled());
|
||||
assert(type != NULL);
|
||||
assert(uri != NULL);
|
||||
assert(name != NULL);
|
||||
|
||||
if (*name == 0)
|
||||
return NULL;
|
||||
|
||||
sqlite3_reset(sticker_stmt_get);
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_get, 1, type, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_get, 2, uri, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_get, 3, name, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = sqlite3_step(sticker_stmt_get);
|
||||
} while (ret == SQLITE_BUSY);
|
||||
|
||||
if (ret == SQLITE_ROW) {
|
||||
/* record found */
|
||||
value = g_strdup((const char*)sqlite3_column_text(sticker_stmt_get, 0));
|
||||
} else if (ret == SQLITE_DONE) {
|
||||
/* no record found */
|
||||
value = NULL;
|
||||
} else {
|
||||
/* error */
|
||||
g_warning("sqlite3_step() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_reset(sticker_stmt_get);
|
||||
sqlite3_clear_bindings(sticker_stmt_get);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool
|
||||
sticker_update_value(const char *type, const char *uri,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(type != NULL);
|
||||
assert(uri != NULL);
|
||||
assert(name != NULL);
|
||||
assert(*name != 0);
|
||||
assert(value != NULL);
|
||||
|
||||
assert(sticker_enabled());
|
||||
|
||||
sqlite3_reset(sticker_stmt_update);
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_update, 1, value, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_update, 2, type, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_update, 3, uri, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_update, 4, name, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = sqlite3_step(sticker_stmt_update);
|
||||
} while (ret == SQLITE_BUSY);
|
||||
|
||||
if (ret != SQLITE_DONE) {
|
||||
g_warning("sqlite3_step() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_changes(sticker_db);
|
||||
|
||||
sqlite3_reset(sticker_stmt_update);
|
||||
sqlite3_clear_bindings(sticker_stmt_update);
|
||||
|
||||
return ret > 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
sticker_insert_value(const char *type, const char *uri,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(type != NULL);
|
||||
assert(uri != NULL);
|
||||
assert(name != NULL);
|
||||
assert(*name != 0);
|
||||
assert(value != NULL);
|
||||
|
||||
assert(sticker_enabled());
|
||||
|
||||
sqlite3_reset(sticker_stmt_insert);
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_insert, 1, type, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_insert, 2, uri, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_insert, 3, name, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_insert, 4, value, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = sqlite3_step(sticker_stmt_insert);
|
||||
} while (ret == SQLITE_BUSY);
|
||||
|
||||
if (ret != SQLITE_DONE) {
|
||||
g_warning("sqlite3_step() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_reset(sticker_stmt_insert);
|
||||
sqlite3_clear_bindings(sticker_stmt_insert);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
sticker_store_value(const char *type, const char *uri,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
assert(sticker_enabled());
|
||||
assert(type != NULL);
|
||||
assert(uri != NULL);
|
||||
assert(name != NULL);
|
||||
assert(value != NULL);
|
||||
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(sticker_enabled());
|
||||
assert(type != NULL);
|
||||
assert(uri != NULL);
|
||||
|
||||
sqlite3_reset(sticker_stmt_delete);
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_delete, 1, type, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sqlite3_bind_text(sticker_stmt_delete, 2, uri, -1, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
g_warning("sqlite3_bind_text() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = sqlite3_step(sticker_stmt_delete);
|
||||
} while (ret == SQLITE_BUSY);
|
||||
|
||||
if (ret != SQLITE_DONE) {
|
||||
g_warning("sqlite3_step() failed: %s",
|
||||
sqlite3_errmsg(sticker_db));
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_reset(sticker_stmt_delete);
|
||||
sqlite3_clear_bindings(sticker_stmt_delete);
|
||||
|
||||
return true;
|
||||
}
|
86
src/sticker.h
Normal file
86
src/sticker.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 STICKER_H
|
||||
#define STICKER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Opens the sticker database (if path is not NULL).
|
||||
*/
|
||||
void
|
||||
sticker_global_init(const char *path);
|
||||
|
||||
/**
|
||||
* Close the sticker database.
|
||||
*/
|
||||
void
|
||||
sticker_global_finish(void);
|
||||
|
||||
/**
|
||||
* Returns true if the sticker database is configured and available.
|
||||
*/
|
||||
bool
|
||||
sticker_enabled(void);
|
||||
|
||||
/**
|
||||
* Returns one value from an object's sticker record. The caller must
|
||||
* free the return value with g_free().
|
||||
*/
|
||||
char *
|
||||
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);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user