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"
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
41static void xmms_medialib_client_remove_entry (xmms_medialib_t *medialib, gint32 entry, xmms_error_t *error);
42gchar *xmms_medialib_url_encode (const gchar *path);
43static gboolean xmms_medialib_check_id_in_session (xmms_medialib_entry_t entry, xmms_medialib_session_t *session);
44
45static void xmms_medialib_client_add_entry (xmms_medialib_t *, const gchar *, xmms_error_t *);
46static void xmms_medialib_client_move_entry (xmms_medialib_t *, gint32 entry, const gchar *, xmms_error_t *);
47static void xmms_medialib_client_import_path (xmms_medialib_t *medialib, const gchar *path, xmms_error_t *error);
48static void xmms_medialib_client_rehash (xmms_medialib_t *medialib, gint32 id, xmms_error_t *error);
49static 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);
50static 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);
51static void xmms_medialib_client_remove_property (xmms_medialib_t *medialib, gint32 entry, const gchar *source, const gchar *key, xmms_error_t *error);
52static GTree *xmms_medialib_client_get_info (xmms_medialib_t *medialib, gint32 id, xmms_error_t *err);
53static 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 */
71struct 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 */
83struct 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 */
108static xmms_medialib_t *medialib;
109
110static 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 */
118static xmms_medialib_session_t *global_medialib_session;
119
120/** This protects the above global session */
121static GMutex *global_medialib_session_mutex;
122
123static GMutex *xmms_medialib_debug_mutex;
124static GHashTable *xmms_medialib_debug_hash;
125
126static void
127xmms_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
144static gint
145source_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
169static int
170xmms_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
204static void
205xmms_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
231static void
232xmms_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
252static int
253add_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
260guint32
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
287xmms_medialib_session_new (const char *file, int line)
288{
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;
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
369 "file://" SHAREDDIR
370 "/unreal_dm-free.music.and.free.beer.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{
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
424void
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
450static int
451xmms_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
465static int
466xmms_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
483xmmsv_t *
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
515gchar *
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 */
542gint
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 */
570gboolean
573 const gchar *property, gint value)
574{
575 return xmms_medialib_entry_property_set_int_source (session, entry,
576 property, value,
578}
579
580
581gboolean
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 */
620gboolean
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
631gboolean
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
673void
675{
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 */
687void
689{
692 XMMSV_TYPE_INT32, entry);
693}
694
695static void
696xmms_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
709void
711{
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
722static xmms_medialib_entry_t xmms_medialib_entry_new_insert (xmms_medialib_session_t *session, guint32 id, const char *url, xmms_error_t *error);
723
724static void
725process_file (xmms_medialib_session_t *session,
726 const gchar *playlist,
727 gint32 pos,
728 const gchar *path,
729 xmms_error_t *error)
730{
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
746static gint
747cmp_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;
754 return 0;
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! */
766static gboolean
767process_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
801void
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
824static void
825xmms_medialib_client_rehash (xmms_medialib_t *medialib, gint32 id, xmms_error_t *error)
826{
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 */
859void
860xmms_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 */
872void
873xmms_medialib_insert_recursive (xmms_medialib_t *medialib, const gchar *playlist,
874 gint32 pos, const gchar *path,
875 xmms_error_t *error)
876{
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
912static void
913xmms_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
920xmms_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
1024gint32
1025xmms_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
1040static void
1041xmms_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
1057static gboolean
1058xmms_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
1074static gboolean
1075xmms_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
1101static GList *
1102xmms_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
1144static GTree *
1145xmms_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. */
1181GList *
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
1203static GTree *
1204xmms_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
1226static gboolean
1227select_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
1243static void
1244xmms_medialib_client_add_entry (xmms_medialib_t *medialib, const gchar *url,
1245 xmms_error_t *error)
1246{
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 */
1268static void
1269xmms_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
1290static void
1291xmms_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
1315static void
1316xmms_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
1338static void
1339xmms_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
1356static void
1357xmms_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 */
1379GList *
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
1401gboolean
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
1418static gboolean
1419xmms_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
1457guint
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
1474gboolean
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 :( */
1522gchar *
1523xmms_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}
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
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
int xmmsv_dict_entry_get_string(xmmsv_t *val, const char *key, const char **r)
int xmmsv_dict_entry_get_int(xmmsv_t *val, const char *key, int32_t *r)
xmmsv_t * xmmsv_new_dict(void)
Allocates a new dict xmmsv_t.
Definition: value.c:268
void xmms_mediainfo_reader_wakeup(xmms_mediainfo_reader_t *mr)
Wake the reader thread and start process the entries.
Definition: mediainfo.c:111
GList * xmms_medialib_select(xmms_medialib_session_t *session, const gchar *query, xmms_error_t *error)
Get a list of GHashTables 's that matches the query.
Definition: medialib.c:1380
xmms_medialib_session_t * _xmms_medialib_begin(gboolean write, const char *file, int line)
Session handling.
Definition: medialib.c:385
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
guint32 xmms_medialib_source_to_id(xmms_medialib_session_t *session, const gchar *source)
Definition: medialib.c:261
void xmms_medialib_entry_cleanup(xmms_medialib_session_t *session, xmms_medialib_entry_t entry)
Definition: medialib.c:802
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
void xmms_medialib_add_recursive(xmms_medialib_t *medialib, const gchar *playlist, const gchar *path, xmms_error_t *error)
Definition: medialib.c:860
GList * xmms_medialib_info_list(xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
Definition: medialib.c:1182
xmms_medialib_t * xmms_medialib_init(xmms_playlist_t *playlist)
Initialize the medialib and open the database file.
Definition: medialib.c:315
#define XMMS_MEDIALIB_RETRV_PROPERTY_SQL
Retrieve a property from an entry.
Definition: medialib.c:481
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
void xmms_medialib_entry_remove(xmms_medialib_entry_t entry)
Remove a medialib entry from the database.
Definition: medialib.c:710
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
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
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
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
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
void xmms_medialib_entry_send_update(xmms_medialib_entry_t entry)
Trigger a update signal to the client.
Definition: medialib.c:674
#define XMMS_MEDIALIB_SOURCE_SERVER_ID
Definition: medialib.c:142
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
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
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:425
void xmms_medialib_entry_send_added(xmms_medialib_entry_t entry)
Trigger an added siginal to the client.
Definition: medialib.c:688
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
xmms_mediainfo_reader_t * xmms_playlist_mediainfo_reader_get(xmms_playlist_t *playlist)
returns pointer to mediainfo reader.
Definition: playlist.c:1547
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
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
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
gboolean xmms_sqlite_exec(sqlite3 *sql, const char *query,...)
A query that can't retrieve results.
Definition: sqlite.c:564
gboolean xmms_sqlite_query_int(sqlite3 *sql, gint32 *out, const gchar *query,...)
Definition: sqlite.c:773
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
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
sqlite3 * xmms_sqlite_open()
Open a database or create a new one.
Definition: sqlite.c:513
void xmms_sqlite_close(sqlite3 *sql)
Close database and free all resources used.
Definition: sqlite.c:793
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
xmmsv_t * xmmsv_ref(xmmsv_t *val)
References the xmmsv_t.
Definition: value.c:288
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:180
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:863
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
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
@ XMMSV_TYPE_DICT
Definition: xmmsv_general.h:43
@ XMMSV_TYPE_INT32
Definition: xmmsv_general.h:38
@ XMMSV_TYPE_STRING
Definition: xmmsv_general.h:39
guint xmms_medialib_num_not_resolved(xmms_medialib_session_t *session)
Definition: medialib.c:1458
xmms_medialib_entry_t xmms_medialib_entry_not_resolved_get(xmms_medialib_session_t *session)
Definition: medialib.c:1440
#define GOODCHAR(a)
Definition: medialib.c:1512
gboolean xmms_medialib_decode_url(char *url)
Definition: medialib.c:1475
gboolean xmms_medialib_check_id(xmms_medialib_entry_t entry)
Definition: medialib.c:1402
gchar * xmms_medialib_url_encode(const gchar *path)
Definition: medialib.c:1523
struct xmms_medialib_St xmms_medialib_t
Definition: xmms_medialib.h:27
GList * xmms_xform_browse(const gchar *url, xmms_error_t *error)
Definition: xform.c:277
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
#define xmms_log_info(fmt,...)
Definition: xmms_log.h:34
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define xmms_log_fatal(fmt,...)
Definition: xmms_log.h:33
#define xmms_medialib_begin_write()
#define xmms_medialib_begin()
struct xmms_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
#define XMMS_MEDIALIB_ENTRY_PROPERTY_LMOD
Definition: xmms_medialib.h:44
#define XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED
Definition: xmms_medialib.h:56
#define xmms_medialib_entry_status_set(session, e, st)
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
#define XMMS_MEDIALIB_ENTRY_PROPERTY_URL
Definition: xmms_medialib.h:29
#define XMMS_MEDIALIB_ENTRY_PROPERTY_ADDED
Definition: xmms_medialib.h:54
#define XMMS_MEDIALIB_ENTRY_PROPERTY_ID
Definition: xmms_medialib.h:28
#define XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS
Definition: xmms_medialib.h:67
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
struct xmms_mediainfo_reader_St xmms_mediainfo_reader_t
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
#define xmms_object_ref(obj)
Definition: xmms_object.h:103
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
#define XMMS_BUILD_PATH(...)
Definition: xmms_utils.h:4
@ XMMS_ERROR_GENERIC
@ XMMS_ERROR_NOENT
@ XMMS_MEDIALIB_ENTRY_STATUS_REHASH
@ XMMS_MEDIALIB_ENTRY_STATUS_NEW
@ XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_UPDATE
@ XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_ADDED