XMMS2
medialib.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2011 XMMS2 Team
3  *
4  * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  */
16 
17 #include "xmms_configuration.h"
18 #include "xmmspriv/xmms_medialib.h"
19 #include "xmmspriv/xmms_xform.h"
20 #include "xmmspriv/xmms_utils.h"
21 #include "xmms/xmms_error.h"
22 #include "xmms/xmms_config.h"
23 #include "xmms/xmms_object.h"
24 #include "xmms/xmms_ipc.h"
25 #include "xmms/xmms_log.h"
26 
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #include <glib.h>
31 #include <time.h>
32 
33 #include <sqlite3.h>
34 
35 /**
36  * @file
37  * Medialib is a metainfo cache that is searchable.
38  */
39 
40 
41 static void xmms_medialib_client_remove_entry (xmms_medialib_t *medialib, gint32 entry, xmms_error_t *error);
42 gchar *xmms_medialib_url_encode (const gchar *path);
43 static gboolean xmms_medialib_check_id_in_session (xmms_medialib_entry_t entry, xmms_medialib_session_t *session);
44 
45 static void xmms_medialib_client_add_entry (xmms_medialib_t *, const gchar *, xmms_error_t *);
46 static void xmms_medialib_client_move_entry (xmms_medialib_t *, gint32 entry, const gchar *, xmms_error_t *);
47 static void xmms_medialib_client_import_path (xmms_medialib_t *medialib, const gchar *path, xmms_error_t *error);
48 static void xmms_medialib_client_rehash (xmms_medialib_t *medialib, gint32 id, xmms_error_t *error);
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);
52 static GTree *xmms_medialib_client_get_info (xmms_medialib_t *medialib, gint32 id, xmms_error_t *err);
53 static gint32 xmms_medialib_client_get_id (xmms_medialib_t *medialib, const gchar *url, xmms_error_t *error);
54 
55 #include "medialib_ipc.c"
56 
57 /**
58  *
59  * @defgroup Medialib Medialib
60  * @ingroup XMMSServer
61  * @brief Medialib caches metadata
62  *
63  * Controls metadata storage.
64  *
65  * @{
66  */
67 
68 /**
69  * Medialib structure
70  */
71 struct xmms_medialib_St {
72  xmms_object_t object;
73  /** The current playlist */
74  xmms_playlist_t *playlist;
75 
76  GMutex *source_lock;
77  GHashTable *sources;
78 };
79 
80 /**
81  * This is handed out by xmms_medialib_begin()
82  */
83 struct xmms_medialib_session_St {
84  xmms_medialib_t *medialib;
85 
86  /** The SQLite handler */
87  sqlite3 *sql;
88 
89  /** debug file */
90  const gchar *file;
91  /** debug line number */
92  gint line;
93 
94  /* Write or read lock, true if write */
95  gboolean write;
96 
97  gint next_id;
98 };
99 
100 
101 /**
102  * Ok, so the functions are written with reentrency in mind, but
103  * we choose to have a global medialib object here. It will be
104  * much easier, and I don't see the real use of multiple medialibs
105  * right now. This could be changed by removing this global one
106  * and altering the function callers...
107  */
108 static xmms_medialib_t *medialib;
109 
110 static const char source_pref[] =
111  "server:client/*:plugin/playlist:plugin/id3v2:plugin/segment:plugin/*";
112 
113 /**
114  * This is only used if we are using a older version of sqlite.
115  * The reason for this is that we must have a global session, due to some
116  * strange limitiations in older sqlite libraries.
117  */
118 static xmms_medialib_session_t *global_medialib_session;
119 
120 /** This protects the above global session */
121 static GMutex *global_medialib_session_mutex;
122 
123 static GMutex *xmms_medialib_debug_mutex;
124 static GHashTable *xmms_medialib_debug_hash;
125 
126 static void
127 xmms_medialib_destroy (xmms_object_t *object)
128 {
129  xmms_medialib_t *mlib = (xmms_medialib_t *)object;
130  if (global_medialib_session) {
131  xmms_sqlite_close (global_medialib_session->sql);
132  g_free (global_medialib_session);
133  }
134  g_mutex_free (mlib->source_lock);
135  g_hash_table_destroy (mlib->sources);
136  g_mutex_free (global_medialib_session_mutex);
137 
138  xmms_medialib_unregister_ipc_commands ();
139 }
140 
141 #define XMMS_MEDIALIB_SOURCE_SERVER "server"
142 #define XMMS_MEDIALIB_SOURCE_SERVER_ID 1
143 
144 static gint
145 source_match_pattern (const gchar *source, const gchar *pattern,
146  gint pattern_len)
147 {
148  /* check whether we need to keep a wildcard in mind when matching
149  * the strings.
150  */
151  if (pattern_len > 0 && pattern[pattern_len - 1] == '*') {
152  /* if the asterisk is the first character of the pattern,
153  * it obviously accepts anything.
154  */
155  if (pattern_len == 1) {
156  return TRUE;
157  }
158 
159  /* otherwise we have to compare the characters just up to the
160  * asterisk.
161  */
162  return !g_ascii_strncasecmp (source, pattern, pattern_len - 1);
163  }
164 
165  /* there's no wildcards, so just compare all of the characters. */
166  return !g_ascii_strncasecmp (pattern, source, pattern_len);
167 }
168 
169 static int
170 xmms_find_match_index (gint source, const gchar *pref, xmms_medialib_t *mlib)
171 {
172  gchar *source_name, *colon;
173  gint i = 0;
174 
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);
178 
179  do {
180  gsize len;
181 
182  colon = strchr (pref, ':');
183 
184  /* get the length of this substring */
185  len = colon ? colon - pref : strlen (pref);
186 
187  /* check whether the substring matches */
188  if (source_match_pattern (source_name, pref, len)) {
189  return i;
190  }
191 
192  /* prepare for next iteration */
193  if (colon) {
194  pref = colon + 1;
195  }
196  i++;
197 
198  /* if we just processed the final substring, then we're done */
199  } while (colon);
200 
201  return i;
202 }
203 
204 static void
205 xmms_sqlite_source_pref_binary (sqlite3_context *context, int args,
206  sqlite3_value **val)
207 {
208  gint source;
209  const gchar *pref;
210  xmms_medialib_t *mlib;
211 
212  mlib = sqlite3_user_data (context);
213 
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);
217  return;
218  }
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);
222  return;
223  }
224 
225  source = sqlite3_value_int (val[0]);
226  pref = (const gchar *) sqlite3_value_text (val[1]);
227 
228  sqlite3_result_int (context, xmms_find_match_index (source, pref, mlib));
229 }
230 
231 static void
232 xmms_sqlite_source_pref_unary (sqlite3_context *context, int args,
233  sqlite3_value **val)
234 {
235  gint source;
236  xmms_medialib_t *mlib;
237 
238  mlib = sqlite3_user_data (context);
239 
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);
243  return;
244  }
245 
246  source = sqlite3_value_int (val[0]);
247 
248  sqlite3_result_int (context,
249  xmms_find_match_index (source, source_pref, mlib));
250 }
251 
252 static int
253 add_to_source (void *hash, int columns, char **vals, char **cols)
254 {
255  int source = strtol (vals[0], NULL, 10);
256  g_hash_table_insert ((GHashTable*)hash, GINT_TO_POINTER (source), g_strdup (vals[1]));
257  return 0;
258 }
259 
260 guint32
262  const gchar *source)
263 {
264  gint32 ret = 0;
265  g_return_val_if_fail (source, 0);
266 
267  xmms_sqlite_query_int (session->sql, &ret,
268  "SELECT id FROM Sources WHERE source=%Q",
269  source);
270  if (ret == 0) {
271  xmms_sqlite_exec (session->sql,
272  "INSERT INTO Sources (source) VALUES (%Q)", source);
273  xmms_sqlite_query_int (session->sql, &ret,
274  "SELECT id FROM Sources WHERE source=%Q",
275  source);
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);
280  }
281 
282  return ret;
283 }
284 
285 
287 xmms_medialib_session_new (const char *file, int line)
288 {
289  xmms_medialib_session_t *session;
290 
291  session = g_new0 (xmms_medialib_session_t, 1);
292  session->medialib = medialib;
293  session->file = file;
294  session->line = line;
295  session->sql = xmms_sqlite_open ();
296 
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);
301 
302  return session;
303 }
304 
305 
306 
307 /**
308  * Initialize the medialib and open the database file.
309  *
310  * @param playlist the current playlist pointer
311  * @returns TRUE if successful and FALSE if there was a problem
312  */
313 
316 {
317  gchar *path;
318  xmms_medialib_session_t *session;
319  gboolean create;
320 
321  medialib = xmms_object_new (xmms_medialib_t, xmms_medialib_destroy);
322  medialib->playlist = playlist;
323 
324  xmms_medialib_register_ipc_commands (XMMS_OBJECT (medialib));
325 
326  path = XMMS_BUILD_PATH ("medialib.db");
327 
328  xmms_config_property_register ("medialib.path", path, NULL, NULL);
329  xmms_config_property_register ("medialib.analyze_on_startup", "0", NULL, NULL);
330  xmms_config_property_register ("medialib.allow_remote_fs",
331  "0", NULL, NULL);
332 
333  g_free (path);
334 
335 
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;
339 
340  /* init the database */
341  xmms_sqlite_create (&create);
342 
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 ("********************************************************************");
348  /** Create a global session, this is only used when the sqlite version
349  * doesn't support concurrent sessions */
350  global_medialib_session = xmms_medialib_session_new ("global", 0);
351  }
352 
353  global_medialib_session_mutex = g_mutex_new ();
354 
355  /**
356  * this dummy just wants to put the default song in the playlist
357  */
358  medialib->source_lock = g_mutex_new ();
359  medialib->sources = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
360 
361  session = xmms_medialib_begin_write ();
362  sqlite3_exec (session->sql, "SELECT id, source FROM Sources",
363  add_to_source, medialib->sources, NULL);
364 
365  if (create) {
366  xmms_error_t error;
367 
368  xmms_medialib_entry_new (session,
369  "file://" SHAREDDIR
370  "/mind.in.a.box-lament_snipplet.ogg",
371  &error);
372  /* A default playlist containing that song has been created
373  * with the mlib.
374  */
375  }
376 
377  xmms_medialib_end (session);
378 
379  return medialib;
380 }
381 
382 /** Session handling */
383 
385 _xmms_medialib_begin (gboolean write, const char *file, int line)
386 {
387  xmms_medialib_session_t *session;
388 
389  {
390  gchar *r;
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);
394  if (r) {
395  xmms_log_fatal ("Medialib session begun recursivly at %s:%d, after %s", file, line, r);
396  } else {
397  g_hash_table_insert (xmms_medialib_debug_hash, me,
398  g_strdup_printf ("%s:%d", file, line));
399  }
400  g_mutex_unlock (xmms_medialib_debug_mutex);
401  }
402  if (global_medialib_session) {
403  /** This will only happen when OLD_SQLITE_VERSION is set. */
404  g_mutex_lock (global_medialib_session_mutex);
405  return global_medialib_session;
406  }
407 
408  session = xmms_medialib_session_new (file, line);
409  xmms_object_ref (XMMS_OBJECT (medialib));
410  session->write = write;
411 
412  if (write) {
413  /* Start a exclusive transaction */
414  if (!xmms_sqlite_exec (session->sql, "BEGIN EXCLUSIVE TRANSACTION")) {
415  xmms_log_error ("transaction failed!");
416  }
417  }
418 
419  session->next_id = -1;
420 
421  return session;
422 }
423 
424 void
426 {
427  g_return_if_fail (session);
428 
429  {
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);
434  }
435 
436  if (session->write) {
437  xmms_sqlite_exec (session->sql, "COMMIT");
438  }
439 
440  if (session == global_medialib_session) {
441  g_mutex_unlock (global_medialib_session_mutex);
442  return;
443  }
444 
445  xmms_sqlite_close (session->sql);
446  xmms_object_unref (XMMS_OBJECT (session->medialib));
447  g_free (session);
448 }
449 
450 static int
451 xmms_medialib_string_cb (xmmsv_t **row, gpointer udata)
452 {
453  gchar **str = udata;
454  const gchar *buf;
455 
456  if (row && row[0] && xmmsv_get_type (row[0]) == XMMSV_TYPE_STRING) {
457  xmmsv_get_string (row[0], &buf);
458  *str = g_strdup (buf);
459  } else
460  XMMS_DBG ("Expected string but got something else!");
461 
462  return 0;
463 }
464 
465 static int
466 xmms_medialib_value_cb (xmmsv_t **row, gpointer udata)
467 {
468  xmmsv_t **ret = udata;
469 
470  *ret = xmmsv_ref (row[0]);
471 
472  return 0;
473 }
474 
475 /**
476  * Retrieve a property from an entry
477  *
478  * @see xmms_medialib_entry_property_get_str
479  */
480 
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"
482 
483 xmmsv_t *
485  xmms_medialib_entry_t entry,
486  const gchar *property)
487 {
488  xmmsv_t *ret = NULL;
489 
490  g_return_val_if_fail (property, NULL);
491  g_return_val_if_fail (session, NULL);
492 
493  if (!strcmp (property, XMMS_MEDIALIB_ENTRY_PROPERTY_ID)) {
494  ret = xmmsv_new_int (entry);
495  } else {
496  xmms_sqlite_query_array (session->sql, xmms_medialib_value_cb,
498  property, entry, source_pref);
499  }
500 
501  return ret;
502 }
503 
504 /**
505  * Retrieve a property from an entry.
506  *
507  * @param session The medialib session to be used for the transaction.
508  * @param entry Entry to query.
509  * @param property The property to extract. Strings passed should
510  * be defined in medialib.h
511  *
512  * @returns Newly allocated gchar that needs to be freed with g_free
513  */
514 
515 gchar *
517  xmms_medialib_entry_t entry,
518  const gchar *property)
519 {
520  gchar *ret = NULL;
521 
522  g_return_val_if_fail (property, NULL);
523  g_return_val_if_fail (session, NULL);
524 
525  xmms_sqlite_query_array (session->sql, xmms_medialib_string_cb, &ret,
527  property, entry, source_pref);
528 
529  return ret;
530 }
531 
532 /**
533  * Retrieve a property as a int from a entry.
534  *
535  * @param session The medialib session to be used for the transaction.
536  * @param entry Entry to query.
537  * @param property The property to extract. Strings passed should
538  * be defined in medialib.h
539  *
540  * @returns Property as integer, or -1 if it doesn't exist.
541  */
542 gint
544  xmms_medialib_entry_t entry,
545  const gchar *property)
546 {
547  gint32 ret = -1;
548 
549  g_return_val_if_fail (property, -1);
550  g_return_val_if_fail (session, -1);
551 
552  xmms_sqlite_query_int (session->sql, &ret,
554  property, entry, source_pref);
555 
556  return ret;
557 }
558 
559 /**
560  * Set a entry property to a new value, overwriting the old value.
561  *
562  * @param session The medialib session to be used for the transaction.
563  * @param entry Entry to alter.
564  * @param property The property to extract. Strings passed should
565  * be defined in medialib.h
566  * @param value gint with the new value, will be copied in to the medialib
567  *
568  * @returns TRUE on success and FALSE on failure.
569  */
570 gboolean
572  xmms_medialib_entry_t entry,
573  const gchar *property, gint value)
574 {
575  return xmms_medialib_entry_property_set_int_source (session, entry,
576  property, value,
578 }
579 
580 
581 gboolean
583  xmms_medialib_entry_t entry,
584  const gchar *property, gint value,
585  guint32 source)
586 {
587  gboolean ret;
588 
589  g_return_val_if_fail (property, FALSE);
590  g_return_val_if_fail (session, FALSE);
591 
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);
595 
596  return FALSE;
597  }
598 
599  ret = xmms_sqlite_exec (session->sql,
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);
604 
605  return ret;
606 
607 }
608 
609 /**
610  * Set a entry property to a new value, overwriting the old value.
611  *
612  * @param session The medialib session to be used for the transaction.
613  * @param entry Entry to alter.
614  * @param property The property to extract. Strings passed should
615  * be defined in medialib.h
616  * @param value gchar with the new value, will be copied in to the medialib
617  *
618  * @returns TRUE on success and FALSE on failure.
619  */
620 gboolean
622  xmms_medialib_entry_t entry,
623  const gchar *property, const gchar *value)
624 {
625  return xmms_medialib_entry_property_set_str_source (session, entry,
626  property, value,
628 }
629 
630 
631 gboolean
633  xmms_medialib_entry_t entry,
634  const gchar *property, const gchar *value,
635  guint32 source)
636 {
637  gboolean ret;
638 
639  g_return_val_if_fail (property, FALSE);
640  g_return_val_if_fail (session, FALSE);
641 
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);
644  return FALSE;
645  }
646 
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);
650 
651  return FALSE;
652  }
653 
654  ret = xmms_sqlite_exec (session->sql,
655  "INSERT OR REPLACE INTO Media "
656  "(id, value, intval, key, source) VALUES "
657  "(%d, %Q, NULL, %Q, %d)",
658  entry, value, property, source);
659 
660  return ret;
661 
662 }
663 
664 
665 /**
666  * Trigger a update signal to the client. This should be called
667  * when important information in the entry has been changed and
668  * should be visible to the user.
669  *
670  * @param entry Entry to signal a update for.
671  */
672 
673 void
675 {
676  xmms_object_emit_f (XMMS_OBJECT (medialib),
678  XMMSV_TYPE_INT32, entry);
679 }
680 
681 /**
682  * Trigger an added siginal to the client. This should be
683  * called when a new entry has been added to the medialib
684  *
685  * @param entry Entry to signal an add for.
686  */
687 void
689 {
690  xmms_object_emit_f (XMMS_OBJECT (medialib),
692  XMMSV_TYPE_INT32, entry);
693 }
694 
695 static void
696 xmms_medialib_client_remove_entry (xmms_medialib_t *medialib,
697  gint32 entry, xmms_error_t *error)
698 {
700 }
701 
702 /**
703  * Remove a medialib entry from the database
704  *
705  * @param session The medialib session to be used for the transaction.
706  * @param entry Entry to remove
707  */
708 
709 void
711 {
712  xmms_medialib_session_t *session;
713 
714  session = xmms_medialib_begin_write ();
715  xmms_sqlite_exec (session->sql, "DELETE FROM Media WHERE id=%d", entry);
716  xmms_medialib_end (session);
717 
718  /** @todo safe ? */
719  xmms_playlist_remove_by_entry (medialib->playlist, entry);
720 }
721 
722 static xmms_medialib_entry_t xmms_medialib_entry_new_insert (xmms_medialib_session_t *session, guint32 id, const char *url, xmms_error_t *error);
723 
724 static void
725 process_file (xmms_medialib_session_t *session,
726  const gchar *playlist,
727  gint32 pos,
728  const gchar *path,
729  xmms_error_t *error)
730 {
731  xmms_medialib_entry_t entry;
732 
733  entry = xmms_medialib_entry_new_encoded (session, path, error);
734 
735  if (entry && playlist != NULL) {
736  if (pos >= 0) {
737  xmms_playlist_insert_entry (session->medialib->playlist,
738  playlist, pos, entry, error);
739  } else {
740  xmms_playlist_add_entry (session->medialib->playlist,
741  playlist, entry, error);
742  }
743  }
744 }
745 
746 static gint
747 cmp_val (gconstpointer a, gconstpointer b)
748 {
749  xmmsv_t *v1, *v2;
750  const gchar *s1, *s2;
751  v1 = (xmmsv_t *) a;
752  v2 = (xmmsv_t *) b;
753  if (xmmsv_get_type (v1) != XMMSV_TYPE_DICT)
754  return 0;
755  if (xmmsv_get_type (v2) != XMMSV_TYPE_DICT)
756  return 0;
757 
758  xmmsv_dict_entry_get_string (v1, "path", &s1);
759  xmmsv_dict_entry_get_string (v2, "path", &s2);
760 
761  return strcmp (s1, s2);
762 }
763 
764 /* code ported over from CLI's "radd" command. */
765 /* note that the returned file list is reverse-sorted! */
766 static gboolean
767 process_dir (const gchar *directory,
768  GList **ret,
769  xmms_error_t *error)
770 {
771  GList *list;
772 
773  list = xmms_xform_browse (directory, error);
774  if (!list) {
775  return FALSE;
776  }
777 
778  list = g_list_sort (list, cmp_val);
779 
780  while (list) {
781  xmmsv_t *val = list->data;
782  const gchar *str;
783  gint isdir;
784 
785  xmmsv_dict_entry_get_string (val, "path", &str);
786  xmmsv_dict_entry_get_int (val, "isdir", &isdir);
787 
788  if (isdir == 1) {
789  process_dir (str, ret, error);
790  } else {
791  *ret = g_list_prepend (*ret, g_strdup (str));
792  }
793 
794  xmmsv_unref (val);
795  list = g_list_delete_link (list, list);
796  }
797 
798  return TRUE;
799 }
800 
801 void
803  xmms_medialib_entry_t entry)
804 {
805  xmms_sqlite_exec (session->sql,
806  "DELETE FROM Media WHERE id=%d AND source=%d "
807  "AND key NOT IN (%Q, %Q, %Q, %Q, %Q)",
808  entry,
815 
816  xmms_sqlite_exec (session->sql,
817  "DELETE FROM Media WHERE id=%d AND source IN "
818  "(SELECT id FROM Sources WHERE source LIKE 'plugin/%%' "
819  "AND source != 'plugin/playlist')",
820  entry);
821 
822 }
823 
824 static void
825 xmms_medialib_client_rehash (xmms_medialib_t *medialib, gint32 id, xmms_error_t *error)
826 {
828  xmms_medialib_session_t *session;
829 
830  session = xmms_medialib_begin_write ();
831 
832  if (id) {
833  xmms_sqlite_exec (session->sql,
834  "UPDATE Media SET value = '%d', intval = %d "
835  "WHERE key='%s' AND id=%d",
839  } else {
840  xmms_sqlite_exec (session->sql,
841  "UPDATE Media SET value = '%d', intval = %d "
842  "WHERE key='%s'",
846  }
847 
848  xmms_medialib_end (session);
849 
850  mr = xmms_playlist_mediainfo_reader_get (medialib->playlist);
852 
853 }
854 
855 /* Recursively add entries under the given path to the medialib,
856  * optionally adding them to a playlist if the playlist argument is
857  * not NULL.
858  */
859 void
860 xmms_medialib_add_recursive (xmms_medialib_t *medialib, const gchar *playlist,
861  const gchar *path, xmms_error_t *error)
862 {
863  /* Just called insert with negative pos to append */
864  xmms_medialib_insert_recursive (medialib, playlist, -1, path, error);
865 }
866 
867 /* Recursively adding entries under the given path to the medialib,
868  * optionally insert them into a playlist at a given position if the
869  * playlist argument is not NULL. If the position is negative, entries
870  * are appended to the playlist.
871  */
872 void
873 xmms_medialib_insert_recursive (xmms_medialib_t *medialib, const gchar *playlist,
874  gint32 pos, const gchar *path,
875  xmms_error_t *error)
876 {
877  xmms_medialib_session_t *session;
878  GList *first, *list = NULL, *n;
879 
880  g_return_if_fail (medialib);
881  g_return_if_fail (path);
882 
883  /* Allocate our first list node manually here. The following call
884  * to process_dir() will prepend all other nodes, so afterwards
885  * "first" will point to the last node of the list... see below.
886  */
887  first = list = g_list_alloc ();
888 
889  process_dir (path, &list, error);
890 
891  XMMS_DBG ("taking the transaction!");
892  session = xmms_medialib_begin_write ();
893 
894  /* We now want to iterate the list in the order in which the nodes
895  * were added, ie in reverse order. Thankfully we stored a pointer
896  * to the last node in the list before, which saves us an expensive
897  * g_list_last() call now. Increase pos each time to retain order.
898  */
899  for (n = first->prev; n; n = g_list_previous (n)) {
900  process_file (session, playlist, pos, n->data, error);
901  if (pos >= 0)
902  pos++;
903  g_free (n->data);
904  }
905 
906  g_list_free (list);
907 
908  XMMS_DBG ("and we are done!");
909  xmms_medialib_end (session);
910 }
911 
912 static void
913 xmms_medialib_client_import_path (xmms_medialib_t *medialib, const gchar *path,
914  xmms_error_t *error)
915 {
916  xmms_medialib_add_recursive (medialib, NULL, path, error);
917 }
918 
920 xmms_medialib_entry_new_insert (xmms_medialib_session_t *session,
921  guint32 id,
922  const char *url,
923  xmms_error_t *error)
924 {
926  guint source;
927 
929 
930  if (!xmms_sqlite_exec (session->sql,
931  "INSERT INTO Media (id, key, value, source) VALUES "
932  "(%d, '%s', %Q, %d)",
934  source)) {
935  xmms_error_set (error, XMMS_ERROR_GENERIC,
936  "Sql error/corruption inserting url");
937  return 0;
938  }
939 
941  mr = xmms_playlist_mediainfo_reader_get (medialib->playlist);
943 
944  return 1;
945 
946 }
947 
948 /**
949  * @internal
950  */
953  const char *url, xmms_error_t *error)
954 {
955  gint id = 0;
956  guint ret = 0;
957  guint source;
958 
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);
962 
964 
965  xmms_sqlite_query_int (session->sql, &id,
966  "SELECT id AS value FROM Media "
967  "WHERE key='%s' AND value=%Q AND source=%d",
969  source);
970 
971  if (id) {
972  ret = id;
973  } else {
974  if (session->next_id <= 0 &&
975  !xmms_sqlite_query_int (session->sql, &session->next_id,
976  "SELECT IFNULL(MAX (id),0)+1 FROM Media")) {
977  xmms_error_set (error, XMMS_ERROR_GENERIC,
978  "SQL error/corruption selecting max(id)");
979  return 0;
980  }
981 
982  ret = session->next_id++;
983 
984  if (!xmms_medialib_entry_new_insert (session, ret, url, error))
985  return 0;
986  }
987 
989  return ret;
990 
991 }
992 
993 /**
994  * Welcome to a function that should be called something else.
995  * Returns a entry for a URL, if the URL is already in the medialib
996  * the current entry will be returned otherwise a new one will be
997  * created and returned.
998  *
999  * @todo rename to something better?
1000  *
1001  * @param session The medialib session to be used for the transaction.
1002  * @param url URL to add/retrieve from the medialib
1003  * @param error If an error occurs, it will be stored in there.
1004  *
1005  * @returns Entry mapped to the URL
1006  */
1009 {
1010  gchar *enc_url;
1012 
1013  enc_url = xmms_medialib_url_encode (url);
1014  if (!enc_url)
1015  return 0;
1016 
1017  res = xmms_medialib_entry_new_encoded (session, enc_url, error);
1018 
1019  g_free (enc_url);
1020 
1021  return res;
1022 }
1023 
1024 gint32
1025 xmms_medialib_client_get_id (xmms_medialib_t *medialib, const gchar *url,
1026  xmms_error_t *error)
1027 {
1028  gint32 id = 0;
1030 
1031  xmms_sqlite_query_int (session->sql, &id,
1032  "SELECT id AS value FROM Media WHERE key='%s' AND value=%Q AND source=%d",
1035  xmms_medialib_end (session);
1036 
1037  return id;
1038 }
1039 
1040 static void
1041 xmms_medialib_tree_add_tuple (GTree *tree, const char *key,
1042  const char *source, xmmsv_t *value)
1043 {
1044  xmmsv_t *keytreeval;
1045 
1046  /* Find (or insert) subtree matching the prop key */
1047  keytreeval = (xmmsv_t *) g_tree_lookup (tree, key);
1048  if (!keytreeval) {
1049  keytreeval = xmmsv_new_dict ();
1050  g_tree_insert (tree, g_strdup (key), keytreeval);
1051  }
1052 
1053  /* Replace (or insert) value matching the prop source */
1054  xmmsv_dict_set (keytreeval, source, value);
1055 }
1056 
1057 static gboolean
1058 xmms_medialib_list_cb (xmmsv_t **row, gpointer udata)
1059 {
1060  GList **list = (GList**)udata;
1061 
1062  /* Source */
1063  *list = g_list_prepend (*list, xmmsv_ref (row[0]));
1064 
1065  /* Key */
1066  *list = g_list_prepend (*list, xmmsv_ref (row[1]));
1067 
1068  /* Value */
1069  *list = g_list_prepend (*list, xmmsv_ref (row[2]));
1070 
1071  return TRUE;
1072 }
1073 
1074 static gboolean
1075 xmms_medialib_tree_cb (xmmsv_t **row, gpointer udata)
1076 {
1077  const char *key, *source;
1078  xmmsv_t *value;
1079  GTree **tree = (GTree**)udata;
1080 
1081  xmmsv_get_string (row[0], &source);
1082  xmmsv_get_string (row[1], &key);
1083  value = row[2];
1084 
1085  xmms_medialib_tree_add_tuple (*tree, key, source, value);
1086 
1087  return TRUE;
1088 }
1089 
1090 /**
1091  * Convert a entry and all properties to a hashtable that
1092  * could be feed to the client or somewhere else in the daemon.
1093  *
1094  * @param session The medialib session to be used for the transaction.
1095  * @param entry Entry to convert.
1096  *
1097  * @returns Newly allocated hashtable with newly allocated strings
1098  * make sure to free them all.
1099  */
1100 
1101 static GList *
1102 xmms_medialib_entry_to_list (xmms_medialib_session_t *session, xmms_medialib_entry_t entry)
1103 {
1104  GList *ret = NULL;
1105  gboolean s;
1106 
1107  g_return_val_if_fail (session, NULL);
1108  g_return_val_if_fail (entry, NULL);
1109 
1110  s = xmms_sqlite_query_array (session->sql, xmms_medialib_list_cb, &ret,
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 "
1115  "WHERE m.id=%d",
1116  entry);
1117  if (!s || !ret) {
1118  return NULL;
1119  }
1120 
1121  /* Source */
1122  ret = g_list_prepend (ret, xmmsv_new_string ("server"));
1123 
1124  /* Key */
1125  ret = g_list_prepend (ret, xmmsv_new_string ("id"));
1126 
1127  /* Value */
1128  ret = g_list_prepend (ret, xmmsv_new_int (entry));
1129 
1130  return g_list_reverse (ret);
1131 }
1132 
1133 /**
1134  * Convert a entry and all properties to a key-source-value tree that
1135  * could be feed to the client or somewhere else in the daemon.
1136  *
1137  * @param session The medialib session to be used for the transaction.
1138  * @param entry Entry to convert.
1139  *
1140  * @returns Newly allocated tree with newly allocated strings
1141  * make sure to free them all.
1142  */
1143 
1144 static GTree *
1145 xmms_medialib_entry_to_tree (xmms_medialib_session_t *session, xmms_medialib_entry_t entry)
1146 {
1147  GTree *ret;
1148  xmmsv_t *v_entry;
1149  gboolean s;
1150 
1151  g_return_val_if_fail (session, NULL);
1152  g_return_val_if_fail (entry, NULL);
1153 
1154  if (!xmms_medialib_check_id_in_session (entry, session)) {
1155  return NULL;
1156  }
1157 
1158  ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
1159  (GDestroyNotify) xmmsv_unref);
1160 
1161  s = xmms_sqlite_query_array (session->sql, xmms_medialib_tree_cb,
1162  &ret,
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 "
1167  "WHERE m.id=%d",
1168  entry);
1169  if (!s || !ret) {
1170  return NULL;
1171  }
1172 
1173  v_entry = xmmsv_new_int (entry);
1174  xmms_medialib_tree_add_tuple (ret, "id", "server", v_entry);
1175  xmmsv_unref (v_entry);
1176 
1177  return ret;
1178 }
1179 
1180 /* Legacy, still used by collections. */
1181 GList *
1183 {
1184  xmms_medialib_session_t *session;
1185  GList *ret = NULL;
1186 
1187  if (!id) {
1188  xmms_error_set (err, XMMS_ERROR_NOENT, "No such entry, 0");
1189  } else {
1190  session = xmms_medialib_begin ();
1191  ret = xmms_medialib_entry_to_list (session, id);
1192  xmms_medialib_end (session);
1193 
1194  if (!ret) {
1195  xmms_error_set (err, XMMS_ERROR_NOENT,
1196  "Could not retrieve info for that entry!");
1197  }
1198  }
1199 
1200  return ret;
1201 }
1202 
1203 static GTree *
1204 xmms_medialib_client_get_info (xmms_medialib_t *medialib, gint32 id,
1205  xmms_error_t *err)
1206 {
1207  xmms_medialib_session_t *session;
1208  GTree *ret = NULL;
1209 
1210  if (!id) {
1211  xmms_error_set (err, XMMS_ERROR_NOENT, "No such entry, 0");
1212  } else {
1213  session = xmms_medialib_begin ();
1214  ret = xmms_medialib_entry_to_tree (session, id);
1215  xmms_medialib_end (session);
1216 
1217  if (!ret) {
1218  xmms_error_set (err, XMMS_ERROR_NOENT,
1219  "Could not retrieve info for that entry!");
1220  }
1221  }
1222 
1223  return ret;
1224 }
1225 
1226 static gboolean
1227 select_callback (xmmsv_t *row, gpointer udata)
1228 {
1229  GList **l = (GList **) udata;
1230  *l = g_list_prepend (*l, row);
1231  return TRUE;
1232 }
1233 
1234 /**
1235  * Add a entry to the medialib. Calls #xmms_medialib_entry_new and then
1236  * wakes up the mediainfo_reader in order to resolve the metadata.
1237  *
1238  * @param medialib Medialib pointer
1239  * @param url URL to add
1240  * @param error In case of error this will be filled.
1241  */
1242 
1243 static void
1244 xmms_medialib_client_add_entry (xmms_medialib_t *medialib, const gchar *url,
1245  xmms_error_t *error)
1246 {
1247  xmms_medialib_entry_t entry;
1248  xmms_medialib_session_t *session;
1249 
1250  g_return_if_fail (medialib);
1251  g_return_if_fail (url);
1252 
1253  session = xmms_medialib_begin_write ();
1254 
1255  entry = xmms_medialib_entry_new_encoded (session, url, error);
1256 
1257  xmms_medialib_end (session);
1258 }
1259 
1260 /**
1261  * Changes the URL of an entry in the medialib.
1262  *
1263  * @param medialib Medialib pointer
1264  * @param entry entry to modify
1265  * @param url URL to change to
1266  * @param error In case of error this will be filled.
1267  */
1268 static void
1269 xmms_medialib_client_move_entry (xmms_medialib_t *medialib, gint32 entry,
1270  const gchar *url, xmms_error_t *error)
1271 {
1272  const gchar *key = XMMS_MEDIALIB_ENTRY_PROPERTY_URL;
1273  guint32 sourceid = XMMS_MEDIALIB_SOURCE_SERVER_ID;
1274  gchar *enc_url;
1275 
1276  xmms_medialib_session_t *session;
1277 
1278  enc_url = xmms_medialib_url_encode (url);
1279 
1280  session = xmms_medialib_begin_write ();
1281  xmms_medialib_entry_property_set_str_source (session, entry, key, enc_url,
1282  sourceid);
1283  xmms_medialib_end (session);
1284 
1285  g_free (enc_url);
1286 
1288 }
1289 
1290 static void
1291 xmms_medialib_client_set_property_string (xmms_medialib_t *medialib,
1292  gint32 entry, const gchar *source,
1293  const gchar *key, const gchar *value,
1294  xmms_error_t *error)
1295 {
1296  guint32 sourceid;
1297  xmms_medialib_session_t *session;
1298 
1299  if (g_ascii_strcasecmp (source, "server") == 0) {
1300  xmms_error_set (error, XMMS_ERROR_GENERIC,
1301  "Can't write to source server!");
1302  return;
1303  }
1304 
1305  session = xmms_medialib_begin_write ();
1306  sourceid = xmms_medialib_source_to_id (session, source);
1307 
1308  xmms_medialib_entry_property_set_str_source (session, entry, key, value,
1309  sourceid);
1310  xmms_medialib_end (session);
1311 
1313 }
1314 
1315 static void
1316 xmms_medialib_client_set_property_int (xmms_medialib_t *medialib, gint32 entry,
1317  const gchar *source, const gchar *key,
1318  gint32 value, xmms_error_t *error)
1319 {
1320  guint32 sourceid;
1321  xmms_medialib_session_t *session;
1322 
1323  if (g_ascii_strcasecmp (source, "server") == 0) {
1324  xmms_error_set (error, XMMS_ERROR_GENERIC,
1325  "Can't write to source server!");
1326  return;
1327  }
1328 
1329  session = xmms_medialib_begin_write ();
1330  sourceid = xmms_medialib_source_to_id (session, source);
1331  xmms_medialib_entry_property_set_int_source (session, entry, key, value,
1332  sourceid);
1333  xmms_medialib_end (session);
1334 
1336 }
1337 
1338 static void
1339 xmms_medialib_property_remove (xmms_medialib_t *medialib, gint32 entry,
1340  const gchar *source, const gchar *key,
1341  xmms_error_t *error)
1342 {
1343  guint32 sourceid;
1344 
1346  sourceid = xmms_medialib_source_to_id (session, source);
1347  xmms_sqlite_exec (session->sql,
1348  "DELETE FROM Media WHERE source=%d AND key='%s' AND "
1349  "id=%d",
1350  sourceid, key, entry);
1351  xmms_medialib_end (session);
1352 
1354 }
1355 
1356 static void
1357 xmms_medialib_client_remove_property (xmms_medialib_t *medialib, gint32 entry,
1358  const gchar *source, const gchar *key,
1359  xmms_error_t *error)
1360 {
1361  if (g_ascii_strcasecmp (source, "server") == 0) {
1362  xmms_error_set (error, XMMS_ERROR_GENERIC,
1363  "Can't remove properties set by the server!");
1364  return;
1365  }
1366 
1367  return xmms_medialib_property_remove (medialib, entry, source, key, error);
1368 }
1369 
1370 /**
1371  * Get a list of GHashTables 's that matches the query.
1372  *
1373  * @param session The medialib session to be used for the transaction.
1374  * @param query SQL query that should be executed.
1375  * @param error In case of error this will be filled.
1376  * @returns GList containing GHashTables. Caller are responsible to
1377  * free all memory.
1378  */
1379 GList *
1381  const gchar *query, xmms_error_t *error)
1382 {
1383  GList *res = NULL;
1384  gint ret;
1385 
1386  g_return_val_if_fail (query, 0);
1387  g_return_val_if_fail (session, 0);
1388 
1389  ret = xmms_sqlite_query_table (session->sql, select_callback,
1390  (void *)&res, error, "%s", query);
1391 
1392  return ret ? g_list_reverse (res) : NULL;
1393 }
1394 
1395 /** @} */
1396 
1397 /**
1398  * @internal
1399  */
1400 
1401 gboolean
1403 {
1404  xmms_medialib_session_t *session;
1405  gboolean ret;
1406 
1407  session = xmms_medialib_begin ();
1408  ret = xmms_medialib_check_id_in_session (entry, session);
1409  xmms_medialib_end (session);
1410 
1411  return ret;
1412 }
1413 
1414 /**
1415  * @internal
1416  */
1417 
1418 static gboolean
1419 xmms_medialib_check_id_in_session (xmms_medialib_entry_t entry,
1420  xmms_medialib_session_t *session)
1421 {
1422  gint c = 0;
1423 
1424  if (!xmms_sqlite_query_int (session->sql, &c,
1425  "SELECT COUNT(id) FROM Media WHERE id=%d",
1426  entry)) {
1427  return FALSE;
1428  }
1429 
1430  return c > 0;
1431 }
1432 
1433 
1434 /**
1435  * @internal
1436  * Get the next unresolved entry. Used by the mediainfo reader..
1437  */
1438 
1441 {
1442  gint32 ret = 0;
1443 
1444  g_return_val_if_fail (session, 0);
1445 
1446  xmms_sqlite_query_int (session->sql, &ret,
1447  "SELECT id FROM Media WHERE key='%s' "
1448  "AND source=%d AND intval IN (%d, %d) LIMIT 1",
1453 
1454  return ret;
1455 }
1456 
1457 guint
1459 {
1460  gint ret;
1461  g_return_val_if_fail (session, 0);
1462 
1463  xmms_sqlite_query_int (session->sql, &ret,
1464  "SELECT COUNT(id) AS value FROM Media WHERE "
1465  "key='%s' AND intval IN (%d, %d) AND source=%d",
1470 
1471  return ret;
1472 }
1473 
1474 gboolean
1476 {
1477  int i = 0, j = 0;
1478 
1479  g_return_val_if_fail (url, TRUE);
1480 
1481  while (url[i]) {
1482  unsigned char chr = url[i++];
1483 
1484  if (chr == '+') {
1485  url[j++] = ' ';
1486  } else if (chr == '%') {
1487  char ts[3];
1488  char *t;
1489 
1490  ts[0] = url[i++];
1491  if (!ts[0])
1492  return FALSE;
1493  ts[1] = url[i++];
1494  if (!ts[1])
1495  return FALSE;
1496  ts[2] = '\0';
1497 
1498  url[j++] = strtoul (ts, &t, 16);
1499  if (t != &ts[2])
1500  return FALSE;
1501  } else {
1502  url[j++] = chr;
1503  }
1504  }
1505 
1506  url[j] = '\0';
1507 
1508  return TRUE;
1509 }
1510 
1511 
1512 #define GOODCHAR(a) ((((a) >= 'a') && ((a) <= 'z')) || \
1513  (((a) >= 'A') && ((a) <= 'Z')) || \
1514  (((a) >= '0') && ((a) <= '9')) || \
1515  ((a) == ':') || \
1516  ((a) == '/') || \
1517  ((a) == '-') || \
1518  ((a) == '.') || \
1519  ((a) == '_'))
1520 
1521 /* we don't share code here with medialib because we want to use g_malloc :( */
1522 gchar *
1523 xmms_medialib_url_encode (const gchar *path)
1524 {
1525  static gchar hex[16] = "0123456789abcdef";
1526  gchar *res;
1527  int i = 0, j = 0;
1528 
1529  res = g_malloc (strlen (path) * 3 + 1);
1530  if (!res)
1531  return NULL;
1532 
1533  while (path[i]) {
1534  guchar chr = path[i++];
1535  if (GOODCHAR (chr)) {
1536  res[j++] = chr;
1537  } else if (chr == ' ') {
1538  res[j++] = '+';
1539  } else {
1540  res[j++] = '%';
1541  res[j++] = hex[((chr & 0xf0) >> 4)];
1542  res[j++] = hex[(chr & 0x0f)];
1543  }
1544  }
1545 
1546  res[j] = '\0';
1547 
1548  return res;
1549 }
gchar * xmms_medialib_url_encode(const gchar *path)
Definition: medialib.c:1523
sqlite3 * xmms_sqlite_open()
Open a database or create a new one.
Definition: sqlite.c:513
xmms_medialib_session_t * _xmms_medialib_begin(gboolean write, const char *file, int line)
Session handling.
Definition: medialib.c:385
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
struct xmms_mediainfo_reader_St xmms_mediainfo_reader_t
gboolean xmms_sqlite_create(gboolean *create)
Definition: sqlite.c:365
gboolean xmms_sqlite_query_array(sqlite3 *sql, xmms_medialib_row_array_method_t method, gpointer udata, const gchar *query,...)
Definition: sqlite.c:747
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
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.
Definition: value.c:1752
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed...
Definition: value.c:303
void xmms_medialib_add_recursive(xmms_medialib_t *medialib, const gchar *playlist, const gchar *path, xmms_error_t *error)
Definition: medialib.c:860
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
void xmms_medialib_insert_recursive(xmms_medialib_t *medialib, const gchar *playlist, gint32 pos, const gchar *path, xmms_error_t *error)
Definition: medialib.c:873
gint xmms_medialib_entry_property_get_int(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property)
Retrieve a property as a int from a entry.
Definition: medialib.c:543
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
#define XMMS_MEDIALIB_SOURCE_SERVER_ID
Definition: medialib.c:142
void xmms_medialib_entry_cleanup(xmms_medialib_session_t *session, xmms_medialib_entry_t entry)
Definition: medialib.c:802
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.
Definition: playlist.c:1062
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:180
gboolean xmms_medialib_entry_property_set_int_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, gint value, guint32 source)
Definition: medialib.c:582
gboolean xmms_medialib_entry_property_set_str(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, const gchar *value)
Set a entry property to a new value, overwriting the old value.
Definition: medialib.c:621
guint32 xmms_medialib_source_to_id(xmms_medialib_session_t *session, const gchar *source)
Definition: medialib.c:261
xmmsv_t * xmmsv_new_dict(void)
Allocates a new dict xmmsv_t.
Definition: value.c:268
GList * xmms_medialib_select(xmms_medialib_session_t *session, const gchar *query, xmms_error_t *error)
Get a list of GHashTables &#39;s that matches the query.
Definition: medialib.c:1380
gboolean xmms_sqlite_query_int(sqlite3 *sql, gint32 *out, const gchar *query,...)
Definition: sqlite.c:773
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
xmmsv_t * xmms_medialib_entry_property_get_value(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property)
Definition: medialib.c:484
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.
Definition: sqlite.c:599
GList * xmms_medialib_info_list(xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
Definition: medialib.c:1182
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition: object.c:256
#define XMMS_MEDIALIB_RETRV_PROPERTY_SQL
Retrieve a property from an entry.
Definition: medialib.c:481
xmms_medialib_t * xmms_medialib_init(xmms_playlist_t *playlist)
Initialize the medialib and open the database file.
Definition: medialib.c:315
int xmmsv_dict_entry_get_int(xmmsv_t *val, const char *key, int32_t *r)
xmms_medialib_entry_t xmms_medialib_entry_new_encoded(xmms_medialib_session_t *session, const char *url, xmms_error_t *error)
Definition: medialib.c:952
#define XMMS_MEDIALIB_ENTRY_PROPERTY_LMOD
Definition: xmms_medialib.h:44
#define XMMS_MEDIALIB_ENTRY_PROPERTY_ADDED
Definition: xmms_medialib.h:54
gboolean xmms_medialib_check_id(xmms_medialib_entry_t entry)
Definition: medialib.c:1402
void xmms_mediainfo_reader_wakeup(xmms_mediainfo_reader_t *mr)
Wake the reader thread and start process the entries.
Definition: mediainfo.c:111
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
#define XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS
Definition: xmms_medialib.h:67
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.
Definition: playlist.c:886
#define GOODCHAR(a)
Definition: medialib.c:1512
GList * xmms_xform_browse(const gchar *url, xmms_error_t *error)
Definition: xform.c:277
gboolean xmms_playlist_remove_by_entry(xmms_playlist_t *playlist, xmms_medialib_entry_t entry)
Remove all additions of entry in the playlist.
Definition: playlist.c:670
xmms_mediainfo_reader_t * xmms_playlist_mediainfo_reader_get(xmms_playlist_t *playlist)
returns pointer to mediainfo reader.
Definition: playlist.c:1547
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.
Definition: value.c:392
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition: value.c:161
#define XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED
Definition: xmms_medialib.h:56
xmmsv_t * xmmsv_ref(xmmsv_t *val)
References the xmmsv_t.
Definition: value.c:288
struct xmms_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
#define xmms_log_info(fmt,...)
Definition: xmms_log.h:34
void xmms_medialib_entry_send_added(xmms_medialib_entry_t entry)
Trigger an added siginal to the client.
Definition: medialib.c:688
void xmms_medialib_entry_send_update(xmms_medialib_entry_t entry)
Trigger a update signal to the client.
Definition: medialib.c:674
#define xmms_object_ref(obj)
Definition: xmms_object.h:103
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define xmms_medialib_begin_write()
#define xmms_medialib_begin()
struct xmms_medialib_St xmms_medialib_t
Definition: xmms_medialib.h:27
gboolean xmms_medialib_entry_property_set_str_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, const gchar *value, guint32 source)
Definition: medialib.c:632
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:863
gboolean xmms_medialib_decode_url(char *url)
Definition: medialib.c:1475
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
#define xmms_medialib_entry_status_set(session, e, st)
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.
Definition: config.c:334
guint xmms_medialib_num_not_resolved(xmms_medialib_session_t *session)
Definition: medialib.c:1458
gboolean xmms_sqlite_exec(sqlite3 *sql, const char *query,...)
A query that can&#39;t retrieve results.
Definition: sqlite.c:564
gboolean xmms_medialib_entry_property_set_int(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, gint value)
Set a entry property to a new value, overwriting the old value.
Definition: medialib.c:571
#define xmms_log_fatal(fmt,...)
Definition: xmms_log.h:33
void xmms_medialib_entry_remove(xmms_medialib_entry_t entry)
Remove a medialib entry from the database.
Definition: medialib.c:710
xmms_medialib_entry_t xmms_medialib_entry_new(xmms_medialib_session_t *session, const char *url, xmms_error_t *error)
Welcome to a function that should be called something else.
Definition: medialib.c:1008
#define XMMS_BUILD_PATH(...)
Definition: xmms_utils.h:4
#define XMMS_MEDIALIB_ENTRY_PROPERTY_URL
Definition: xmms_medialib.h:29
gchar * xmms_medialib_entry_property_get_str(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property)
Retrieve a property from an entry.
Definition: medialib.c:516
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:425
void xmms_sqlite_close(sqlite3 *sql)
Close database and free all resources used.
Definition: sqlite.c:793
#define XMMS_MEDIALIB_ENTRY_PROPERTY_ID
Definition: xmms_medialib.h:28
xmms_medialib_entry_t xmms_medialib_entry_not_resolved_get(xmms_medialib_session_t *session)
Definition: medialib.c:1440