17 #include "xmms_configuration.h" 49 static void xmms_medialib_client_set_property_string (
xmms_medialib_t *medialib, gint32 entry,
const gchar *source,
const gchar *key,
const gchar *value,
xmms_error_t *error);
50 static void xmms_medialib_client_set_property_int (
xmms_medialib_t *medialib, gint32 entry,
const gchar *source,
const gchar *key, gint32 value,
xmms_error_t *error);
51 static void xmms_medialib_client_remove_property (
xmms_medialib_t *medialib, gint32 entry,
const gchar *source,
const gchar *key,
xmms_error_t *error);
55 #include "medialib_ipc.c" 71 struct xmms_medialib_St {
83 struct xmms_medialib_session_St {
110 static const char source_pref[] =
111 "server:client/*:plugin/playlist:plugin/id3v2:plugin/segment:plugin/*";
121 static GMutex *global_medialib_session_mutex;
123 static GMutex *xmms_medialib_debug_mutex;
124 static GHashTable *xmms_medialib_debug_hash;
130 if (global_medialib_session) {
132 g_free (global_medialib_session);
134 g_mutex_free (mlib->source_lock);
135 g_hash_table_destroy (mlib->sources);
136 g_mutex_free (global_medialib_session_mutex);
138 xmms_medialib_unregister_ipc_commands ();
141 #define XMMS_MEDIALIB_SOURCE_SERVER "server" 142 #define XMMS_MEDIALIB_SOURCE_SERVER_ID 1 145 source_match_pattern (
const gchar *source,
const gchar *pattern,
151 if (pattern_len > 0 && pattern[pattern_len - 1] ==
'*') {
155 if (pattern_len == 1) {
162 return !g_ascii_strncasecmp (source, pattern, pattern_len - 1);
166 return !g_ascii_strncasecmp (pattern, source, pattern_len);
170 xmms_find_match_index (gint source,
const gchar *pref,
xmms_medialib_t *mlib)
172 gchar *source_name, *colon;
175 g_mutex_lock (mlib->source_lock);
176 source_name = g_hash_table_lookup (mlib->sources, GINT_TO_POINTER (source));
177 g_mutex_unlock (mlib->source_lock);
182 colon = strchr (pref,
':');
185 len = colon ? colon - pref : strlen (pref);
188 if (source_match_pattern (source_name, pref, len)) {
205 xmms_sqlite_source_pref_binary (sqlite3_context *context,
int args,
212 mlib = sqlite3_user_data (context);
214 if (sqlite3_value_type (val[0]) != SQLITE_INTEGER) {
215 sqlite3_result_error (context,
"First argument to xmms_source_pref " 216 "should be a integer", -1);
219 if (sqlite3_value_type (val[1]) != SQLITE3_TEXT) {
220 sqlite3_result_error (context,
"Second argument to xmms_source_pref " 221 "should be a string", -1);
225 source = sqlite3_value_int (val[0]);
226 pref = (
const gchar *) sqlite3_value_text (val[1]);
228 sqlite3_result_int (context, xmms_find_match_index (source, pref, mlib));
232 xmms_sqlite_source_pref_unary (sqlite3_context *context,
int args,
238 mlib = sqlite3_user_data (context);
240 if (sqlite3_value_type (val[0]) != SQLITE_INTEGER) {
241 sqlite3_result_error (context,
"First argument to xmms_source_pref " 242 "should be a integer", -1);
246 source = sqlite3_value_int (val[0]);
248 sqlite3_result_int (context,
249 xmms_find_match_index (source, source_pref, mlib));
253 add_to_source (
void *hash,
int columns,
char **vals,
char **cols)
255 int source = strtol (vals[0], NULL, 10);
256 g_hash_table_insert ((GHashTable*)hash, GINT_TO_POINTER (source), g_strdup (vals[1]));
265 g_return_val_if_fail (source, 0);
268 "SELECT id FROM Sources WHERE source=%Q",
272 "INSERT INTO Sources (source) VALUES (%Q)", source);
274 "SELECT id FROM Sources WHERE source=%Q",
276 XMMS_DBG (
"Added source %s with id %d", source, ret);
277 g_mutex_lock (session->medialib->source_lock);
278 g_hash_table_insert (session->medialib->sources, GUINT_TO_POINTER (ret), g_strdup (source));
279 g_mutex_unlock (session->medialib->source_lock);
287 xmms_medialib_session_new (
const char *file,
int line)
292 session->medialib = medialib;
293 session->file = file;
294 session->line = line;
297 sqlite3_create_function (session->sql,
"xmms_source_pref", 2, SQLITE_UTF8,
298 session->medialib, xmms_sqlite_source_pref_binary, NULL, NULL);
299 sqlite3_create_function (session->sql,
"xmms_source_pref", 1, SQLITE_UTF8,
300 session->medialib, xmms_sqlite_source_pref_unary, NULL, NULL);
322 medialib->playlist = playlist;
324 xmms_medialib_register_ipc_commands (
XMMS_OBJECT (medialib));
336 xmms_medialib_debug_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
337 xmms_medialib_debug_mutex = g_mutex_new ();
338 global_medialib_session = NULL;
343 if (!sqlite3_threadsafe ()) {
344 xmms_log_info (
"********************************************************************");
345 xmms_log_info (
"* Using thread hack to compensate for sqlite without threadsafety! *");
346 xmms_log_info (
"* This can be a huge performance penalty - upgrade or recompile *");
347 xmms_log_info (
"********************************************************************");
350 global_medialib_session = xmms_medialib_session_new (
"global", 0);
353 global_medialib_session_mutex = g_mutex_new ();
358 medialib->source_lock = g_mutex_new ();
359 medialib->sources = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
362 sqlite3_exec (session->sql,
"SELECT id, source FROM Sources",
363 add_to_source, medialib->sources, NULL);
370 "/mind.in.a.box-lament_snipplet.ogg",
391 void *me = g_thread_self ();
392 g_mutex_lock (xmms_medialib_debug_mutex);
393 r = g_hash_table_lookup (xmms_medialib_debug_hash, me);
395 xmms_log_fatal (
"Medialib session begun recursivly at %s:%d, after %s", file, line, r);
397 g_hash_table_insert (xmms_medialib_debug_hash, me,
398 g_strdup_printf (
"%s:%d", file, line));
400 g_mutex_unlock (xmms_medialib_debug_mutex);
402 if (global_medialib_session) {
404 g_mutex_lock (global_medialib_session_mutex);
405 return global_medialib_session;
408 session = xmms_medialib_session_new (file, line);
410 session->write = write;
419 session->next_id = -1;
427 g_return_if_fail (session);
430 void *me = g_thread_self ();
431 g_mutex_lock (xmms_medialib_debug_mutex);
432 g_hash_table_remove (xmms_medialib_debug_hash, me);
433 g_mutex_unlock (xmms_medialib_debug_mutex);
436 if (session->write) {
440 if (session == global_medialib_session) {
441 g_mutex_unlock (global_medialib_session_mutex);
451 xmms_medialib_string_cb (
xmmsv_t **row, gpointer udata)
458 *str = g_strdup (buf);
460 XMMS_DBG (
"Expected string but got something else!");
466 xmms_medialib_value_cb (
xmmsv_t **row, gpointer udata)
481 #define XMMS_MEDIALIB_RETRV_PROPERTY_SQL "SELECT IFNULL (intval, value) FROM Media WHERE key=%Q AND id=%d ORDER BY xmms_source_pref(source, %Q) LIMIT 1" 486 const gchar *property)
490 g_return_val_if_fail (property, NULL);
491 g_return_val_if_fail (session, NULL);
498 property, entry, source_pref);
518 const gchar *property)
522 g_return_val_if_fail (property, NULL);
523 g_return_val_if_fail (session, NULL);
527 property, entry, source_pref);
545 const gchar *property)
549 g_return_val_if_fail (property, -1);
550 g_return_val_if_fail (session, -1);
554 property, entry, source_pref);
573 const gchar *property, gint value)
584 const gchar *property, gint value,
589 g_return_val_if_fail (property, FALSE);
590 g_return_val_if_fail (session, FALSE);
592 if (!xmms_medialib_check_id_in_session (entry, session)) {
593 XMMS_DBG (
"Trying to add property to id %d " 594 "that is not yet in the medialib. Denied.", entry);
600 "INSERT OR REPLACE INTO Media " 601 "(id, value, intval, key, source) VALUES " 602 "(%d, '%d', %d, %Q, %d)",
603 entry, value, value, property, source);
623 const gchar *property,
const gchar *value)
634 const gchar *property,
const gchar *value,
639 g_return_val_if_fail (property, FALSE);
640 g_return_val_if_fail (session, FALSE);
642 if (value && !g_utf8_validate (value, -1, NULL)) {
643 XMMS_DBG (
"OOOOOPS! Trying to set property %s to a NON UTF-8 string (%s) I will deny that!", property, value);
647 if (!xmms_medialib_check_id_in_session (entry, session)) {
648 XMMS_DBG (
"Trying to add property to id %d " 649 "that is not yet in the medialib. Denied.", entry);
655 "INSERT OR REPLACE INTO Media " 656 "(id, value, intval, key, source) VALUES " 657 "(%d, %Q, NULL, %Q, %d)",
658 entry, value, property, source);
726 const gchar *playlist,
735 if (entry && playlist != NULL) {
738 playlist, pos, entry, error);
741 playlist, entry, error);
747 cmp_val (gconstpointer a, gconstpointer b)
750 const gchar *s1, *s2;
761 return strcmp (s1, s2);
767 process_dir (
const gchar *directory,
778 list = g_list_sort (list, cmp_val);
789 process_dir (str, ret, error);
791 *ret = g_list_prepend (*ret, g_strdup (str));
795 list = g_list_delete_link (list, list);
806 "DELETE FROM Media WHERE id=%d AND source=%d " 807 "AND key NOT IN (%Q, %Q, %Q, %Q, %Q)",
817 "DELETE FROM Media WHERE id=%d AND source IN " 818 "(SELECT id FROM Sources WHERE source LIKE 'plugin/%%' " 819 "AND source != 'plugin/playlist')",
834 "UPDATE Media SET value = '%d', intval = %d " 835 "WHERE key='%s' AND id=%d",
841 "UPDATE Media SET value = '%d', intval = %d " 874 gint32 pos,
const gchar *path,
878 GList *first, *list = NULL, *n;
880 g_return_if_fail (medialib);
881 g_return_if_fail (path);
887 first = list = g_list_alloc ();
889 process_dir (path, &list, error);
891 XMMS_DBG (
"taking the transaction!");
899 for (n = first->prev; n; n = g_list_previous (n)) {
900 process_file (session, playlist, pos, n->data, error);
913 xmms_medialib_client_import_path (
xmms_medialib_t *medialib,
const gchar *path,
931 "INSERT INTO Media (id, key, value, source) VALUES " 932 "(%d, '%s', %Q, %d)",
936 "Sql error/corruption inserting url");
959 g_return_val_if_fail (url, 0);
960 g_return_val_if_fail (session, 0);
961 g_return_val_if_fail (session->write, 0);
966 "SELECT id AS value FROM Media " 967 "WHERE key='%s' AND value=%Q AND source=%d",
974 if (session->next_id <= 0 &&
976 "SELECT IFNULL(MAX (id),0)+1 FROM Media")) {
978 "SQL error/corruption selecting max(id)");
982 ret = session->next_id++;
984 if (!xmms_medialib_entry_new_insert (session, ret, url, error))
1025 xmms_medialib_client_get_id (
xmms_medialib_t *medialib,
const gchar *url,
1032 "SELECT id AS value FROM Media WHERE key='%s' AND value=%Q AND source=%d",
1041 xmms_medialib_tree_add_tuple (GTree *tree,
const char *key,
1042 const char *source,
xmmsv_t *value)
1047 keytreeval = (
xmmsv_t *) g_tree_lookup (tree, key);
1050 g_tree_insert (tree, g_strdup (key), keytreeval);
1058 xmms_medialib_list_cb (
xmmsv_t **row, gpointer udata)
1060 GList **list = (GList**)udata;
1063 *list = g_list_prepend (*list,
xmmsv_ref (row[0]));
1066 *list = g_list_prepend (*list,
xmmsv_ref (row[1]));
1069 *list = g_list_prepend (*list,
xmmsv_ref (row[2]));
1075 xmms_medialib_tree_cb (
xmmsv_t **row, gpointer udata)
1077 const char *key, *source;
1079 GTree **tree = (GTree**)udata;
1085 xmms_medialib_tree_add_tuple (*tree, key, source, value);
1107 g_return_val_if_fail (session, NULL);
1108 g_return_val_if_fail (entry, NULL);
1111 "SELECT s.source, m.key, " 1112 "IFNULL (m.intval, m.value) " 1113 "FROM Media m LEFT JOIN " 1114 "Sources s ON m.source = s.id " 1130 return g_list_reverse (ret);
1151 g_return_val_if_fail (session, NULL);
1152 g_return_val_if_fail (entry, NULL);
1154 if (!xmms_medialib_check_id_in_session (entry, session)) {
1158 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
1163 "SELECT s.source, m.key, " 1164 "IFNULL (m.intval, m.value) " 1165 "FROM Media m LEFT JOIN " 1166 "Sources s ON m.source = s.id " 1174 xmms_medialib_tree_add_tuple (ret,
"id",
"server", v_entry);
1191 ret = xmms_medialib_entry_to_list (session,
id);
1196 "Could not retrieve info for that entry!");
1214 ret = xmms_medialib_entry_to_tree (session,
id);
1219 "Could not retrieve info for that entry!");
1227 select_callback (
xmmsv_t *row, gpointer udata)
1229 GList **l = (GList **) udata;
1230 *l = g_list_prepend (*l, row);
1244 xmms_medialib_client_add_entry (
xmms_medialib_t *medialib,
const gchar *url,
1250 g_return_if_fail (medialib);
1251 g_return_if_fail (url);
1269 xmms_medialib_client_move_entry (
xmms_medialib_t *medialib, gint32 entry,
1292 gint32 entry,
const gchar *source,
1293 const gchar *key,
const gchar *value,
1299 if (g_ascii_strcasecmp (source,
"server") == 0) {
1301 "Can't write to source server!");
1316 xmms_medialib_client_set_property_int (
xmms_medialib_t *medialib, gint32 entry,
1317 const gchar *source,
const gchar *key,
1323 if (g_ascii_strcasecmp (source,
"server") == 0) {
1325 "Can't write to source server!");
1339 xmms_medialib_property_remove (
xmms_medialib_t *medialib, gint32 entry,
1340 const gchar *source,
const gchar *key,
1348 "DELETE FROM Media WHERE source=%d AND key='%s' AND " 1350 sourceid, key, entry);
1357 xmms_medialib_client_remove_property (
xmms_medialib_t *medialib, gint32 entry,
1358 const gchar *source,
const gchar *key,
1361 if (g_ascii_strcasecmp (source,
"server") == 0) {
1363 "Can't remove properties set by the server!");
1367 return xmms_medialib_property_remove (medialib, entry, source, key, error);
1386 g_return_val_if_fail (query, 0);
1387 g_return_val_if_fail (session, 0);
1390 (
void *)&res, error,
"%s", query);
1392 return ret ? g_list_reverse (res) : NULL;
1408 ret = xmms_medialib_check_id_in_session (entry, session);
1425 "SELECT COUNT(id) FROM Media WHERE id=%d",
1444 g_return_val_if_fail (session, 0);
1447 "SELECT id FROM Media WHERE key='%s' " 1448 "AND source=%d AND intval IN (%d, %d) LIMIT 1",
1461 g_return_val_if_fail (session, 0);
1464 "SELECT COUNT(id) AS value FROM Media WHERE " 1465 "key='%s' AND intval IN (%d, %d) AND source=%d",
1479 g_return_val_if_fail (url, TRUE);
1482 unsigned char chr = url[i++];
1486 }
else if (chr ==
'%') {
1498 url[j++] = strtoul (ts, &t, 16);
1512 #define GOODCHAR(a) ((((a) >= 'a') && ((a) <= 'z')) || \ 1513 (((a) >= 'A') && ((a) <= 'Z')) || \ 1514 (((a) >= '0') && ((a) <= '9')) || \ 1525 static gchar hex[16] =
"0123456789abcdef";
1529 res = g_malloc (strlen (path) * 3 + 1);
1534 guchar chr = path[i++];
1537 }
else if (chr ==
' ') {
1541 res[j++] = hex[((chr & 0xf0) >> 4)];
1542 res[j++] = hex[(chr & 0x0f)];
sqlite3 * xmms_sqlite_open()
Open a database or create a new one.
gboolean xmms_sqlite_create(gboolean *create)
gboolean xmms_sqlite_query_array(sqlite3 *sql, xmms_medialib_row_array_method_t method, gpointer udata, const gchar *query,...)
int xmmsv_dict_set(xmmsv_t *dictv, const char *key, xmmsv_t *val)
Insert an element under the given key in the dict xmmsv_t.
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed...
#define xmms_object_unref(obj)
void xmms_playlist_add_entry(xmms_playlist_t *playlist, const gchar *plname, xmms_medialib_entry_t file, xmms_error_t *err)
Add an entry to the playlist without validating it.
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
xmmsv_t * xmmsv_new_dict(void)
Allocates a new dict xmmsv_t.
gboolean xmms_sqlite_query_int(sqlite3 *sql, gint32 *out, const gchar *query,...)
#define xmms_log_error(fmt,...)
gboolean xmms_sqlite_query_table(sqlite3 *sql, xmms_medialib_row_table_method_t method, gpointer udata, xmms_error_t *error, const gchar *query,...)
Execute a query to the database.
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
int xmmsv_dict_entry_get_int(xmmsv_t *val, const char *key, int32_t *r)
struct xmms_playlist_St xmms_playlist_t
void xmms_playlist_insert_entry(xmms_playlist_t *playlist, const gchar *plname, guint32 pos, xmms_medialib_entry_t file, xmms_error_t *err)
Insert an entry at a given position in the playlist without validating it.
gboolean xmms_playlist_remove_by_entry(xmms_playlist_t *playlist, xmms_medialib_entry_t entry)
Remove all additions of entry in the playlist.
xmms_mediainfo_reader_t * xmms_playlist_mediainfo_reader_get(xmms_playlist_t *playlist)
returns pointer to mediainfo reader.
int xmmsv_dict_entry_get_string(xmmsv_t *val, const char *key, const char **r)
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
xmmsv_t * xmmsv_ref(xmmsv_t *val)
References the xmmsv_t.
#define xmms_log_info(fmt,...)
#define xmms_object_ref(obj)
#define XMMS_DBG(fmt,...)
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
#define xmms_object_new(objtype, destroyfunc)
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
xmms_config_property_t * xmms_config_property_register(const gchar *path, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a new config property.
gboolean xmms_sqlite_exec(sqlite3 *sql, const char *query,...)
A query that can't retrieve results.
#define xmms_log_fatal(fmt,...)
#define XMMS_BUILD_PATH(...)
void xmms_sqlite_close(sqlite3 *sql)
Close database and free all resources used.