00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "xmms_configuration.h"
00018 #include "xmmspriv/xmms_medialib.h"
00019 #include "xmmspriv/xmms_xform.h"
00020 #include "xmmspriv/xmms_utils.h"
00021 #include "xmms/xmms_error.h"
00022 #include "xmms/xmms_config.h"
00023 #include "xmms/xmms_object.h"
00024 #include "xmms/xmms_ipc.h"
00025 #include "xmms/xmms_log.h"
00026
00027 #include <string.h>
00028 #include <stdlib.h>
00029
00030 #include <glib.h>
00031 #include <time.h>
00032
00033 #include <sqlite3.h>
00034
00035
00036
00037
00038
00039
00040
00041 static void xmms_medialib_entry_remove_method (xmms_medialib_t *medialib, guint32 entry, xmms_error_t *error);
00042 gchar *xmms_medialib_url_encode (const gchar *path);
00043 static gboolean xmms_medialib_check_id_in_session (xmms_medialib_entry_t entry, xmms_medialib_session_t *session);
00044
00045 static void xmms_medialib_add_entry (xmms_medialib_t *, const gchar *, xmms_error_t *);
00046 static void xmms_medialib_move_entry (xmms_medialib_t *, guint32 entry, const gchar *, xmms_error_t *);
00047 static void xmms_medialib_path_import (xmms_medialib_t *medialib, const gchar *path, xmms_error_t *error);
00048 static void xmms_medialib_rehash (xmms_medialib_t *medialib, guint32 id, xmms_error_t *error);
00049 static void xmms_medialib_property_set_str_method (xmms_medialib_t *medialib, guint32 entry, const gchar *source, const gchar *key, const gchar *value, xmms_error_t *error);
00050 static void xmms_medialib_property_set_int_method (xmms_medialib_t *medialib, guint32 entry, const gchar *source, const gchar *key, gint32 value, xmms_error_t *error);
00051 static void xmms_medialib_property_remove_method (xmms_medialib_t *medialib, guint32 entry, const gchar *source, const gchar *key, xmms_error_t *error);
00052 static gint32 xmms_medialib_entry_get_id (xmms_medialib_t *medialib, const gchar *url, xmms_error_t *error);
00053
00054
00055 XMMS_CMD_DEFINE (info, xmms_medialib_info, xmms_medialib_t *, DICT, INT32, NONE);
00056 XMMS_CMD_DEFINE (mlib_add, xmms_medialib_add_entry, xmms_medialib_t *, NONE, STRING, NONE);
00057 XMMS_CMD_DEFINE (mlib_remove, xmms_medialib_entry_remove_method, xmms_medialib_t *, NONE, INT32, NONE);
00058 XMMS_CMD_DEFINE (mlib_move, xmms_medialib_move_entry, xmms_medialib_t *, NONE, INT32, STRING);
00059 XMMS_CMD_DEFINE (path_import, xmms_medialib_path_import, xmms_medialib_t *, NONE, STRING, NONE);
00060 XMMS_CMD_DEFINE (rehash, xmms_medialib_rehash, xmms_medialib_t *, NONE, INT32, NONE);
00061 XMMS_CMD_DEFINE (get_id, xmms_medialib_entry_get_id, xmms_medialib_t *, INT32, STRING, NONE);
00062
00063 XMMS_CMD_DEFINE4 (set_property_str, xmms_medialib_property_set_str_method, xmms_medialib_t *, NONE, INT32, STRING, STRING, STRING);
00064 XMMS_CMD_DEFINE4 (set_property_int, xmms_medialib_property_set_int_method, xmms_medialib_t *, NONE, INT32, STRING, STRING, INT32);
00065
00066 XMMS_CMD_DEFINE3 (remove_property, xmms_medialib_property_remove_method, xmms_medialib_t *, NONE, INT32, STRING, STRING);
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 struct xmms_medialib_St {
00083 xmms_object_t object;
00084
00085 xmms_playlist_t *playlist;
00086
00087 GMutex *source_lock;
00088 GHashTable *sources;
00089 };
00090
00091
00092
00093
00094 struct xmms_medialib_session_St {
00095 xmms_medialib_t *medialib;
00096
00097
00098 sqlite3 *sql;
00099
00100
00101 const gchar *file;
00102
00103 gint line;
00104
00105
00106 gboolean write;
00107
00108 gint next_id;
00109 };
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119 static xmms_medialib_t *medialib;
00120
00121 static const char source_pref[] = "server:client/*:plugin/id3v2:plugin/*";
00122
00123
00124
00125
00126
00127
00128 static xmms_medialib_session_t *global_medialib_session;
00129
00130
00131 static GMutex *global_medialib_session_mutex;
00132
00133 static GMutex *xmms_medialib_debug_mutex;
00134 static GHashTable *xmms_medialib_debug_hash;
00135
00136 static void
00137 xmms_medialib_destroy (xmms_object_t *object)
00138 {
00139 xmms_medialib_t *mlib = (xmms_medialib_t *)object;
00140 if (global_medialib_session) {
00141 xmms_sqlite_close (global_medialib_session->sql);
00142 g_free (global_medialib_session);
00143 }
00144 g_mutex_free (mlib->source_lock);
00145 g_hash_table_destroy (mlib->sources);
00146 g_mutex_free (global_medialib_session_mutex);
00147 xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_UPDATE);
00148 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_OUTPUT);
00149 }
00150
00151 #define XMMS_MEDIALIB_SOURCE_SERVER "server"
00152 #define XMMS_MEDIALIB_SOURCE_SERVER_ID 1
00153
00154 static gint
00155 source_match_pattern (const gchar *source, const gchar *pattern,
00156 gint pattern_len)
00157 {
00158
00159
00160
00161 if (pattern_len > 0 && pattern[pattern_len - 1] == '*') {
00162
00163
00164
00165 if (pattern_len == 1) {
00166 return TRUE;
00167 }
00168
00169
00170
00171
00172 return !g_ascii_strncasecmp (source, pattern, pattern_len - 1);
00173 }
00174
00175
00176 return !g_ascii_strncasecmp (pattern, source, pattern_len);
00177 }
00178
00179 static void
00180 xmms_sqlite_source_pref (sqlite3_context *context, int args, sqlite3_value **val)
00181 {
00182 gint source;
00183 const gchar *pref;
00184 xmms_medialib_t *mlib;
00185 gchar *source_name, *colon;
00186 gint i;
00187
00188 mlib = sqlite3_user_data (context);
00189
00190 if (sqlite3_value_type (val[0]) != SQLITE_INTEGER) {
00191 sqlite3_result_error (context, "First argument to xmms_source_pref should be a integer", -1);
00192 return;
00193 }
00194 if (sqlite3_value_type (val[1]) != SQLITE3_TEXT) {
00195 sqlite3_result_error (context, "Second argument to xmms_source_pref should be a string", -1);
00196 return;
00197 }
00198
00199 source = sqlite3_value_int (val[0]);
00200 pref = (const gchar *) sqlite3_value_text (val[1]);
00201
00202 g_mutex_lock (mlib->source_lock);
00203 source_name = g_hash_table_lookup (mlib->sources, GINT_TO_POINTER (source));
00204 g_mutex_unlock (mlib->source_lock);
00205
00206 do {
00207 gsize len;
00208
00209 colon = strchr (pref, ':');
00210
00211
00212 len = colon ? colon - pref : strlen (pref);
00213
00214
00215 if (source_match_pattern (source_name, pref, len)) {
00216 sqlite3_result_int (context, i);
00217 return;
00218 }
00219
00220 if (colon) {
00221 pref = colon + 1;
00222 }
00223
00224
00225 } while (colon);
00226
00227 sqlite3_result_int (context, -1);
00228 }
00229
00230 int
00231 add_to_source (void *hash, int columns, char **vals, char **cols)
00232 {
00233 int source = strtol (vals[0], NULL, 10);
00234 g_hash_table_insert ((GHashTable*)hash, GINT_TO_POINTER (source), g_strdup (vals[1]));
00235 return 0;
00236 }
00237
00238 guint32
00239 xmms_medialib_source_to_id (xmms_medialib_session_t *session,
00240 const gchar *source)
00241 {
00242 gint32 ret = 0;
00243 g_return_val_if_fail (source, 0);
00244
00245 xmms_sqlite_query_int (session->sql, &ret,
00246 "select id from Sources where source=%Q",
00247 source);
00248 if (ret == 0) {
00249 xmms_sqlite_exec (session->sql, "insert into Sources (source) values (%Q)", source);
00250 xmms_sqlite_query_int (session->sql, &ret,
00251 "select id from Sources where source=%Q",
00252 source);
00253 XMMS_DBG ("Added source %s with id %d", source, ret);
00254 g_mutex_lock (session->medialib->source_lock);
00255 g_hash_table_insert (session->medialib->sources, GUINT_TO_POINTER (ret), g_strdup (source));
00256 g_mutex_unlock (session->medialib->source_lock);
00257 }
00258
00259 return ret;
00260 }
00261
00262
00263 static xmms_medialib_session_t *
00264 xmms_medialib_session_new (const char *file, int line)
00265 {
00266 xmms_medialib_session_t *session;
00267
00268 session = g_new0 (xmms_medialib_session_t, 1);
00269 session->medialib = medialib;
00270 session->file = file;
00271 session->line = line;
00272 session->sql = xmms_sqlite_open ();
00273
00274 sqlite3_create_function (session->sql, "xmms_source_pref", 2, SQLITE_UTF8,
00275 session->medialib, xmms_sqlite_source_pref, NULL, NULL);
00276
00277 return session;
00278 }
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289 xmms_medialib_t *
00290 xmms_medialib_init (xmms_playlist_t *playlist)
00291 {
00292 gchar *path;
00293 xmms_medialib_session_t *session;
00294 gboolean create;
00295
00296 medialib = xmms_object_new (xmms_medialib_t, xmms_medialib_destroy);
00297 medialib->playlist = playlist;
00298
00299 xmms_ipc_object_register (XMMS_IPC_OBJECT_MEDIALIB, XMMS_OBJECT (medialib));
00300 xmms_ipc_broadcast_register (XMMS_OBJECT (medialib), XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_ADDED);
00301 xmms_ipc_broadcast_register (XMMS_OBJECT (medialib), XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_UPDATE);
00302
00303 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00304 XMMS_IPC_CMD_INFO,
00305 XMMS_CMD_FUNC (info));
00306 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00307 XMMS_IPC_CMD_ADD_URL,
00308 XMMS_CMD_FUNC (mlib_add));
00309 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00310 XMMS_IPC_CMD_REMOVE_ID,
00311 XMMS_CMD_FUNC (mlib_remove));
00312 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00313 XMMS_IPC_CMD_PATH_IMPORT,
00314 XMMS_CMD_FUNC (path_import));
00315 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00316 XMMS_IPC_CMD_REHASH,
00317 XMMS_CMD_FUNC (rehash));
00318 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00319 XMMS_IPC_CMD_GET_ID,
00320 XMMS_CMD_FUNC (get_id));
00321 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00322 XMMS_IPC_CMD_PROPERTY_SET_STR,
00323 XMMS_CMD_FUNC (set_property_str));
00324 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00325 XMMS_IPC_CMD_PROPERTY_SET_INT,
00326 XMMS_CMD_FUNC (set_property_int));
00327 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00328 XMMS_IPC_CMD_PROPERTY_REMOVE,
00329 XMMS_CMD_FUNC (remove_property));
00330 xmms_object_cmd_add (XMMS_OBJECT (medialib),
00331 XMMS_IPC_CMD_MOVE_URL,
00332 XMMS_CMD_FUNC (mlib_move));
00333
00334 path = XMMS_BUILD_PATH ("medialib.db");
00335
00336 xmms_config_property_register ("medialib.path", path, NULL, NULL);
00337 xmms_config_property_register ("medialib.analyze_on_startup", "0", NULL, NULL);
00338 xmms_config_property_register ("medialib.allow_remote_fs",
00339 "0", NULL, NULL);
00340
00341 g_free (path);
00342
00343
00344 xmms_medialib_debug_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
00345 xmms_medialib_debug_mutex = g_mutex_new ();
00346 global_medialib_session = NULL;
00347
00348
00349 xmms_sqlite_create (&create);
00350
00351 if (!sqlite3_threadsafe ()) {
00352 xmms_log_info ("********************************************************************");
00353 xmms_log_info ("* Using thread hack to compensate for sqlite without threadsafety! *");
00354 xmms_log_info ("* This can be a huge performance penalty - upgrade or recompile *");
00355 xmms_log_info ("********************************************************************");
00356
00357
00358 global_medialib_session = xmms_medialib_session_new ("global", 0);
00359 }
00360
00361 global_medialib_session_mutex = g_mutex_new ();
00362
00363
00364
00365
00366 medialib->source_lock = g_mutex_new ();
00367 medialib->sources = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
00368
00369 session = xmms_medialib_begin_write ();
00370 sqlite3_exec (session->sql, "select id, source from Sources",
00371 add_to_source, medialib->sources, NULL);
00372
00373 if (create) {
00374 xmms_error_t error;
00375
00376 xmms_medialib_entry_new (session,
00377 "file://" SHAREDDIR
00378 "/mind.in.a.box-lament_snipplet.ogg",
00379 &error);
00380
00381
00382
00383 }
00384
00385 xmms_medialib_end (session);
00386
00387 return medialib;
00388 }
00389
00390
00391
00392 xmms_medialib_session_t *
00393 _xmms_medialib_begin (gboolean write, const char *file, int line)
00394 {
00395 xmms_medialib_session_t *session;
00396
00397 {
00398 gchar *r;
00399 void *me = g_thread_self ();
00400 g_mutex_lock (xmms_medialib_debug_mutex);
00401 r = g_hash_table_lookup (xmms_medialib_debug_hash, me);
00402 if (r) {
00403 xmms_log_fatal ("Medialib session begun recursivly at %s:%d, after %s", file, line, r);
00404 } else {
00405 g_hash_table_insert (xmms_medialib_debug_hash, me,
00406 g_strdup_printf ("%s:%d", file, line));
00407 }
00408 g_mutex_unlock (xmms_medialib_debug_mutex);
00409 }
00410 if (global_medialib_session) {
00411
00412 g_mutex_lock (global_medialib_session_mutex);
00413 return global_medialib_session;
00414 }
00415
00416 session = xmms_medialib_session_new (file, line);
00417 xmms_object_ref (XMMS_OBJECT (medialib));
00418 session->write = write;
00419
00420 if (write) {
00421
00422 if (!xmms_sqlite_exec (session->sql, "BEGIN EXCLUSIVE TRANSACTION")) {
00423 xmms_log_error ("transaction failed!");
00424 }
00425 }
00426
00427 session->next_id = -1;
00428
00429 return session;
00430 }
00431
00432 void
00433 xmms_medialib_end (xmms_medialib_session_t *session)
00434 {
00435 g_return_if_fail (session);
00436
00437 {
00438 void *me = g_thread_self ();
00439 g_mutex_lock (xmms_medialib_debug_mutex);
00440 g_hash_table_remove (xmms_medialib_debug_hash, me);
00441 g_mutex_unlock (xmms_medialib_debug_mutex);
00442 }
00443
00444 if (session->write) {
00445 xmms_sqlite_exec (session->sql, "COMMIT");
00446 }
00447
00448 if (session == global_medialib_session) {
00449 g_mutex_unlock (global_medialib_session_mutex);
00450 return;
00451 }
00452
00453 xmms_sqlite_close (session->sql);
00454 xmms_object_unref (XMMS_OBJECT (session->medialib));
00455 g_free (session);
00456 }
00457
00458 static int
00459 xmms_medialib_string_cb (xmmsv_t **row, gpointer udata)
00460 {
00461 gchar **str = udata;
00462 const gchar *buf;
00463
00464 if (row && row[0] && xmmsv_get_type (row[0]) == XMMSV_TYPE_STRING) {
00465 xmmsv_get_string (row[0], &buf);
00466 *str = g_strdup (buf);
00467 } else
00468 XMMS_DBG ("Expected string but got something else!");
00469
00470 return 0;
00471 }
00472
00473 static int
00474 xmms_medialib_value_cb (xmmsv_t **row, gpointer udata)
00475 {
00476 xmmsv_t **ret = udata;
00477
00478 *ret = xmmsv_ref (row[0]);
00479
00480 return 0;
00481 }
00482
00483
00484
00485
00486
00487
00488
00489 #define XMMS_MEDIALIB_RETRV_PROPERTY_SQL "select value from Media where key=%Q and id=%d order by xmms_source_pref(source, %Q) limit 1"
00490
00491 xmmsv_t *
00492 xmms_medialib_entry_property_get_value (xmms_medialib_session_t *session,
00493 xmms_medialib_entry_t entry,
00494 const gchar *property)
00495 {
00496 xmmsv_t *ret = NULL;
00497
00498 g_return_val_if_fail (property, NULL);
00499 g_return_val_if_fail (session, NULL);
00500
00501 if (!strcmp (property, XMMS_MEDIALIB_ENTRY_PROPERTY_ID)) {
00502 ret = xmmsv_new_int (entry);
00503 } else {
00504 xmms_sqlite_query_array (session->sql, xmms_medialib_value_cb,
00505 &ret, XMMS_MEDIALIB_RETRV_PROPERTY_SQL,
00506 property, entry, source_pref);
00507 }
00508
00509 return ret;
00510 }
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523 gchar *
00524 xmms_medialib_entry_property_get_str (xmms_medialib_session_t *session,
00525 xmms_medialib_entry_t entry,
00526 const gchar *property)
00527 {
00528 gchar *ret = NULL;
00529
00530 g_return_val_if_fail (property, NULL);
00531 g_return_val_if_fail (session, NULL);
00532
00533 xmms_sqlite_query_array (session->sql, xmms_medialib_string_cb, &ret,
00534 XMMS_MEDIALIB_RETRV_PROPERTY_SQL,
00535 property, entry, source_pref);
00536
00537 return ret;
00538 }
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550 gint
00551 xmms_medialib_entry_property_get_int (xmms_medialib_session_t *session,
00552 xmms_medialib_entry_t entry,
00553 const gchar *property)
00554 {
00555 gint32 ret = -1;
00556
00557 g_return_val_if_fail (property, -1);
00558 g_return_val_if_fail (session, -1);
00559
00560 xmms_sqlite_query_int (session->sql, &ret,
00561 XMMS_MEDIALIB_RETRV_PROPERTY_SQL,
00562 property, entry, source_pref);
00563
00564 return ret;
00565 }
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578 gboolean
00579 xmms_medialib_entry_property_set_int (xmms_medialib_session_t *session,
00580 xmms_medialib_entry_t entry,
00581 const gchar *property, gint value)
00582 {
00583 return xmms_medialib_entry_property_set_int_source (session, entry,
00584 property, value,
00585 XMMS_MEDIALIB_SOURCE_SERVER_ID);
00586 }
00587
00588
00589 gboolean
00590 xmms_medialib_entry_property_set_int_source (xmms_medialib_session_t *session,
00591 xmms_medialib_entry_t entry,
00592 const gchar *property, gint value,
00593 guint32 source)
00594 {
00595 gboolean ret;
00596
00597 g_return_val_if_fail (property, FALSE);
00598 g_return_val_if_fail (session, FALSE);
00599
00600 if (!xmms_medialib_check_id_in_session (entry, session)) {
00601 XMMS_DBG ("Trying to add property to id %d "
00602 "that is not yet in the medialib. Denied.", entry);
00603
00604 return FALSE;
00605 }
00606
00607 ret = xmms_sqlite_exec (session->sql,
00608 "insert or replace into Media (id, value, key, source) values (%d, %d, %Q, %d)",
00609 entry, value, property, source);
00610
00611 return ret;
00612
00613 }
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626 gboolean
00627 xmms_medialib_entry_property_set_str (xmms_medialib_session_t *session,
00628 xmms_medialib_entry_t entry,
00629 const gchar *property, const gchar *value)
00630 {
00631 return xmms_medialib_entry_property_set_str_source (session, entry,
00632 property, value,
00633 XMMS_MEDIALIB_SOURCE_SERVER_ID);
00634 }
00635
00636
00637 gboolean
00638 xmms_medialib_entry_property_set_str_source (xmms_medialib_session_t *session,
00639 xmms_medialib_entry_t entry,
00640 const gchar *property, const gchar *value,
00641 guint32 source)
00642 {
00643 gboolean ret;
00644
00645 g_return_val_if_fail (property, FALSE);
00646 g_return_val_if_fail (session, FALSE);
00647
00648 if (value && !g_utf8_validate (value, -1, NULL)) {
00649 XMMS_DBG ("OOOOOPS! Trying to set property %s to a NON UTF-8 string (%s) I will deny that!", property, value);
00650 return FALSE;
00651 }
00652
00653 if (!xmms_medialib_check_id_in_session (entry, session)) {
00654 XMMS_DBG ("Trying to add property to id %d "
00655 "that is not yet in the medialib. Denied.", entry);
00656
00657 return FALSE;
00658 }
00659
00660 ret = xmms_sqlite_exec (session->sql,
00661 "insert or replace into Media (id, value, key, source) values (%d, %Q, %Q, %d)",
00662 entry, value, property, source);
00663
00664 return ret;
00665
00666 }
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677 void
00678 xmms_medialib_entry_send_update (xmms_medialib_entry_t entry)
00679 {
00680 xmms_object_emit_f (XMMS_OBJECT (medialib),
00681 XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_UPDATE,
00682 XMMSV_TYPE_INT32, entry);
00683 }
00684
00685
00686
00687
00688
00689
00690
00691 void
00692 xmms_medialib_entry_send_added (xmms_medialib_entry_t entry)
00693 {
00694 xmms_object_emit_f (XMMS_OBJECT (medialib),
00695 XMMS_IPC_SIGNAL_MEDIALIB_ENTRY_ADDED,
00696 XMMSV_TYPE_INT32, entry);
00697 }
00698
00699 static void
00700 xmms_medialib_entry_remove_method (xmms_medialib_t *medialib, guint32 entry, xmms_error_t *error)
00701 {
00702 xmms_medialib_entry_remove (entry);
00703 }
00704
00705
00706
00707
00708
00709
00710
00711
00712 void
00713 xmms_medialib_entry_remove (xmms_medialib_entry_t entry)
00714 {
00715 xmms_medialib_session_t *session;
00716
00717 session = xmms_medialib_begin_write ();
00718 xmms_sqlite_exec (session->sql, "delete from Media where id=%d", entry);
00719 xmms_medialib_end (session);
00720
00721
00722 xmms_playlist_remove_by_entry (medialib->playlist, entry);
00723 }
00724
00725 static xmms_medialib_entry_t xmms_medialib_entry_new_insert (xmms_medialib_session_t *session, guint32 id, const char *url, xmms_error_t *error);
00726
00727 static void
00728 process_file (xmms_medialib_session_t *session,
00729 const gchar *playlist,
00730 gint32 pos,
00731 const gchar *path,
00732 xmms_error_t *error)
00733 {
00734 xmms_medialib_entry_t entry;
00735
00736 entry = xmms_medialib_entry_new_encoded (session, path, error);
00737
00738 if (entry && playlist != NULL) {
00739 if (pos >= 0) {
00740 xmms_playlist_insert_entry (session->medialib->playlist,
00741 playlist, pos, entry, error);
00742 } else {
00743 xmms_playlist_add_entry (session->medialib->playlist,
00744 playlist, entry, error);
00745 }
00746 }
00747 }
00748
00749 static gint
00750 cmp_val (gconstpointer a, gconstpointer b)
00751 {
00752 xmmsv_t *v1, *v2;
00753 const gchar *s1, *s2;
00754 v1 = (xmmsv_t *) a;
00755 v2 = (xmmsv_t *) b;
00756 if (xmmsv_get_type (v1) != XMMSV_TYPE_DICT)
00757 return 0;
00758 if (xmmsv_get_type (v2) != XMMSV_TYPE_DICT)
00759 return 0;
00760
00761 xmmsv_dict_entry_get_string (v1, "path", &s1);
00762 xmmsv_dict_entry_get_string (v2, "path", &s2);
00763
00764 return strcmp (s1, s2);
00765 }
00766
00767
00768 static gboolean
00769 process_dir (const gchar *directory,
00770 GList **ret,
00771 xmms_error_t *error)
00772 {
00773 GList *list;
00774
00775 list = xmms_xform_browse (NULL, directory, error);
00776 if (!list) {
00777 return FALSE;
00778 }
00779
00780 list = g_list_sort (list, cmp_val);
00781
00782 while (list) {
00783 xmmsv_t *val = list->data;
00784 const gchar *str;
00785 gint isdir;
00786
00787 xmmsv_dict_entry_get_string (val, "path", &str);
00788 xmmsv_dict_entry_get_int (val, "isdir", &isdir);
00789
00790 if (isdir == 1) {
00791 process_dir (str, ret, error);
00792 } else {
00793 *ret = g_list_prepend (*ret, g_strdup (str));
00794 }
00795
00796 xmmsv_unref (val);
00797 list = g_list_delete_link (list, list);
00798 }
00799
00800 return TRUE;
00801 }
00802
00803 void
00804 xmms_medialib_entry_cleanup (xmms_medialib_session_t *session,
00805 xmms_medialib_entry_t entry)
00806 {
00807 xmms_sqlite_exec (session->sql,
00808 "delete from Media where id=%d and source=%d "
00809 "and key not in (%Q, %Q, %Q, %Q, %Q)",
00810 entry,
00811 XMMS_MEDIALIB_SOURCE_SERVER_ID,
00812 XMMS_MEDIALIB_ENTRY_PROPERTY_URL,
00813 XMMS_MEDIALIB_ENTRY_PROPERTY_ADDED,
00814 XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS,
00815 XMMS_MEDIALIB_ENTRY_PROPERTY_LMOD,
00816 XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED);
00817
00818 xmms_sqlite_exec (session->sql,
00819 "delete from Media where id=%d and source in "
00820 "(select id from Sources where source like 'plugin/%%' and source != 'plugin/playlist')",
00821 entry);
00822
00823 }
00824
00825 static void
00826 xmms_medialib_rehash (xmms_medialib_t *medialib, guint32 id, xmms_error_t *error)
00827 {
00828 xmms_mediainfo_reader_t *mr;
00829 xmms_medialib_session_t *session;
00830
00831 session = xmms_medialib_begin_write ();
00832
00833 if (id) {
00834 xmms_sqlite_exec (session->sql,
00835 "update Media set value = %d where key='%s' and id=%d",
00836 XMMS_MEDIALIB_ENTRY_STATUS_REHASH,
00837 XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS, id);
00838 } else {
00839 xmms_sqlite_exec (session->sql,
00840 "update Media set value = %d where key='%s'",
00841 XMMS_MEDIALIB_ENTRY_STATUS_REHASH,
00842 XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS);
00843 }
00844
00845 xmms_medialib_end (session);
00846
00847 mr = xmms_playlist_mediainfo_reader_get (medialib->playlist);
00848 xmms_mediainfo_reader_wakeup (mr);
00849
00850 }
00851
00852
00853
00854
00855
00856 void
00857 xmms_medialib_add_recursive (xmms_medialib_t *medialib, const gchar *playlist,
00858 const gchar *path, xmms_error_t *error)
00859 {
00860
00861 xmms_medialib_insert_recursive (medialib, playlist, -1, path, error);
00862 }
00863
00864
00865
00866
00867
00868
00869 void
00870 xmms_medialib_insert_recursive (xmms_medialib_t *medialib, const gchar *playlist,
00871 gint32 pos, const gchar *path,
00872 xmms_error_t *error)
00873 {
00874 xmms_medialib_session_t *session;
00875 GList *first, *list = NULL, *n;
00876
00877 g_return_if_fail (medialib);
00878 g_return_if_fail (path);
00879
00880
00881
00882
00883
00884 first = list = g_list_alloc ();
00885
00886 process_dir (path, &list, error);
00887
00888 XMMS_DBG ("taking the transaction!");
00889 session = xmms_medialib_begin_write ();
00890
00891
00892
00893
00894
00895
00896 for (n = first->prev; n; n = g_list_previous (n)) {
00897 process_file (session, playlist, pos, n->data, error);
00898 g_free (n->data);
00899 }
00900
00901 g_list_free (list);
00902
00903 XMMS_DBG ("and we are done!");
00904 xmms_medialib_end (session);
00905 }
00906
00907 static void
00908 xmms_medialib_path_import (xmms_medialib_t *medialib, const gchar *path,
00909 xmms_error_t *error)
00910 {
00911 xmms_medialib_add_recursive (medialib, NULL, path, error);
00912 }
00913
00914 static xmms_medialib_entry_t
00915 xmms_medialib_entry_new_insert (xmms_medialib_session_t *session,
00916 guint32 id,
00917 const char *url,
00918 xmms_error_t *error)
00919 {
00920 xmms_mediainfo_reader_t *mr;
00921 guint source;
00922
00923 source = XMMS_MEDIALIB_SOURCE_SERVER_ID;
00924
00925 if (!xmms_sqlite_exec (session->sql,
00926 "insert into Media (id, key, value, source) values (%d, '%s', %Q, %d)",
00927 id, XMMS_MEDIALIB_ENTRY_PROPERTY_URL, url,
00928 source)) {
00929 xmms_error_set (error, XMMS_ERROR_GENERIC, "Sql error/corruption inserting url");
00930 return 0;
00931 }
00932
00933 xmms_medialib_entry_status_set (session, id, XMMS_MEDIALIB_ENTRY_STATUS_NEW);
00934 mr = xmms_playlist_mediainfo_reader_get (medialib->playlist);
00935 xmms_mediainfo_reader_wakeup (mr);
00936
00937 return 1;
00938
00939 }
00940
00941
00942
00943
00944 xmms_medialib_entry_t
00945 xmms_medialib_entry_new_encoded (xmms_medialib_session_t *session,
00946 const char *url, xmms_error_t *error)
00947 {
00948 gint id = 0;
00949 guint ret = 0;
00950 guint source;
00951
00952 g_return_val_if_fail (url, 0);
00953 g_return_val_if_fail (session, 0);
00954 g_return_val_if_fail (session->write, 0);
00955
00956 source = XMMS_MEDIALIB_SOURCE_SERVER_ID;
00957
00958 xmms_sqlite_query_int (session->sql, &id,
00959 "select id as value from Media where key='%s' and value=%Q and source=%d",
00960 XMMS_MEDIALIB_ENTRY_PROPERTY_URL, url,
00961 source);
00962
00963 if (id) {
00964 ret = id;
00965 } else {
00966 if (session->next_id <= 0 &&
00967 !xmms_sqlite_query_int (session->sql, &session->next_id,
00968 "SELECT IFNULL(MAX (id),0)+1 FROM Media")) {
00969 xmms_error_set (error, XMMS_ERROR_GENERIC, "Sql error/corruption selecting max(id)");
00970 return 0;
00971 }
00972
00973 ret = session->next_id++;
00974
00975 if (!xmms_medialib_entry_new_insert (session, ret, url, error))
00976 return 0;
00977 }
00978
00979 xmms_medialib_entry_send_added (ret);
00980 return ret;
00981
00982 }
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998 xmms_medialib_entry_t
00999 xmms_medialib_entry_new (xmms_medialib_session_t *session, const char *url, xmms_error_t *error)
01000 {
01001 gchar *enc_url;
01002 xmms_medialib_entry_t res;
01003
01004 enc_url = xmms_medialib_url_encode (url);
01005 if (!enc_url)
01006 return 0;
01007
01008 res = xmms_medialib_entry_new_encoded (session, enc_url, error);
01009
01010 g_free (enc_url);
01011
01012 return res;
01013 }
01014
01015 static gint32
01016 xmms_medialib_entry_get_id (xmms_medialib_t *medialib, const gchar *url,
01017 xmms_error_t *error)
01018 {
01019 gint32 id = 0;
01020 xmms_medialib_session_t *session = xmms_medialib_begin ();
01021
01022 xmms_sqlite_query_int (session->sql, &id,
01023 "select id as value from Media where key='%s' and value=%Q and source=%d",
01024 XMMS_MEDIALIB_ENTRY_PROPERTY_URL, url,
01025 XMMS_MEDIALIB_SOURCE_SERVER_ID);
01026 xmms_medialib_end (session);
01027
01028 return id;
01029 }
01030
01031 static void
01032 xmms_medialib_tree_add_tuple (GTree *tree, const char *key,
01033 const char *source, xmmsv_t *value)
01034 {
01035 xmmsv_t *keytreeval;
01036
01037
01038 keytreeval = (xmmsv_t *) g_tree_lookup (tree, key);
01039 if (!keytreeval) {
01040 keytreeval = xmmsv_new_dict ();
01041 g_tree_insert (tree, g_strdup (key), keytreeval);
01042 }
01043
01044
01045 xmmsv_dict_set (keytreeval, source, value);
01046 }
01047
01048 static gboolean
01049 xmms_medialib_list_cb (xmmsv_t **row, gpointer udata)
01050 {
01051 GList **list = (GList**)udata;
01052
01053
01054 *list = g_list_prepend (*list, xmmsv_ref (row[0]));
01055
01056
01057 *list = g_list_prepend (*list, xmmsv_ref (row[1]));
01058
01059
01060 *list = g_list_prepend (*list, xmmsv_ref (row[2]));
01061
01062 return TRUE;
01063 }
01064
01065 static gboolean
01066 xmms_medialib_tree_cb (xmmsv_t **row, gpointer udata)
01067 {
01068 const char *key, *source;
01069 xmmsv_t *value;
01070 GTree **tree = (GTree**)udata;
01071
01072 xmmsv_get_string (row[0], &source);
01073 xmmsv_get_string (row[1], &key);
01074 value = row[2];
01075
01076 xmms_medialib_tree_add_tuple (*tree, key, source, value);
01077
01078 return TRUE;
01079 }
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092 GList *
01093 xmms_medialib_entry_to_list (xmms_medialib_session_t *session, xmms_medialib_entry_t entry)
01094 {
01095 GList *ret = NULL;
01096 gboolean s;
01097
01098 g_return_val_if_fail (session, NULL);
01099 g_return_val_if_fail (entry, NULL);
01100
01101 s = xmms_sqlite_query_array (session->sql, xmms_medialib_list_cb,
01102 &ret,
01103 "select s.source, m.key, "
01104 "m.value from Media m left join "
01105 "Sources s on m.source = s.id "
01106 "where m.id=%d",
01107 entry);
01108 if (!s || !ret) {
01109 return NULL;
01110 }
01111
01112
01113 ret = g_list_prepend (ret, xmmsv_new_string ("server"));
01114
01115
01116 ret = g_list_prepend (ret, xmmsv_new_string ("id"));
01117
01118
01119 ret = g_list_prepend (ret, xmmsv_new_int (entry));
01120
01121 return g_list_reverse (ret);
01122 }
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135 static GTree *
01136 xmms_medialib_entry_to_tree (xmms_medialib_session_t *session, xmms_medialib_entry_t entry)
01137 {
01138 GTree *ret;
01139 xmmsv_t *v_entry;
01140 gboolean s;
01141
01142 g_return_val_if_fail (session, NULL);
01143 g_return_val_if_fail (entry, NULL);
01144
01145 if (!xmms_medialib_check_id_in_session (entry, session)) {
01146 return NULL;
01147 }
01148
01149 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
01150 (GDestroyNotify) xmmsv_unref);
01151
01152 s = xmms_sqlite_query_array (session->sql, xmms_medialib_tree_cb,
01153 &ret,
01154 "select s.source, m.key, "
01155 "m.value from Media m left join "
01156 "Sources s on m.source = s.id "
01157 "where m.id=%d",
01158 entry);
01159 if (!s || !ret) {
01160 return NULL;
01161 }
01162
01163 v_entry = xmmsv_new_int (entry);
01164 xmms_medialib_tree_add_tuple (ret, "id", "server", v_entry);
01165 xmmsv_unref (v_entry);
01166
01167 return ret;
01168 }
01169
01170
01171 GList *
01172 xmms_medialib_info_list (xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
01173 {
01174 xmms_medialib_session_t *session;
01175 GList *ret = NULL;
01176
01177 if (!id) {
01178 xmms_error_set (err, XMMS_ERROR_NOENT, "No such entry, 0");
01179 } else {
01180 session = xmms_medialib_begin ();
01181 ret = xmms_medialib_entry_to_list (session, id);
01182 xmms_medialib_end (session);
01183
01184 if (!ret) {
01185 xmms_error_set (err, XMMS_ERROR_NOENT,
01186 "Could not retrieve info for that entry!");
01187 }
01188 }
01189
01190 return ret;
01191 }
01192
01193 GTree *
01194 xmms_medialib_info (xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
01195 {
01196 xmms_medialib_session_t *session;
01197 GTree *ret = NULL;
01198
01199 if (!id) {
01200 xmms_error_set (err, XMMS_ERROR_NOENT, "No such entry, 0");
01201 } else {
01202 session = xmms_medialib_begin ();
01203 ret = xmms_medialib_entry_to_tree (session, id);
01204 xmms_medialib_end (session);
01205
01206 if (!ret) {
01207 xmms_error_set (err, XMMS_ERROR_NOENT,
01208 "Could not retrieve info for that entry!");
01209 }
01210 }
01211
01212 return ret;
01213 }
01214
01215 static gboolean
01216 select_callback (xmmsv_t *row, gpointer udata)
01217 {
01218 GList **l = (GList **) udata;
01219 *l = g_list_prepend (*l, row);
01220 return TRUE;
01221 }
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232 static void
01233 xmms_medialib_add_entry (xmms_medialib_t *medialib, const gchar *url,
01234 xmms_error_t *error)
01235 {
01236 xmms_medialib_entry_t entry;
01237 xmms_medialib_session_t *session;
01238
01239 g_return_if_fail (medialib);
01240 g_return_if_fail (url);
01241
01242 session = xmms_medialib_begin_write ();
01243
01244 entry = xmms_medialib_entry_new_encoded (session, url, error);
01245
01246 xmms_medialib_end (session);
01247 }
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257 static void
01258 xmms_medialib_move_entry (xmms_medialib_t *medialib, guint32 entry,
01259 const gchar *url, xmms_error_t *error)
01260 {
01261 const gchar *key = XMMS_MEDIALIB_ENTRY_PROPERTY_URL;
01262 guint32 sourceid = XMMS_MEDIALIB_SOURCE_SERVER_ID;
01263 gchar *enc_url;
01264
01265 xmms_medialib_session_t *session;
01266
01267 enc_url = xmms_medialib_url_encode (url);
01268
01269 session = xmms_medialib_begin_write ();
01270 xmms_medialib_entry_property_set_str_source (session, entry, key, enc_url,
01271 sourceid);
01272 xmms_medialib_end (session);
01273
01274 g_free (enc_url);
01275
01276 xmms_medialib_entry_send_update (entry);
01277 }
01278
01279 static void
01280 xmms_medialib_property_set_str_method (xmms_medialib_t *medialib, guint32 entry,
01281 const gchar *source, const gchar *key,
01282 const gchar *value, xmms_error_t *error)
01283 {
01284 guint32 sourceid;
01285 xmms_medialib_session_t *session;
01286
01287 if (g_ascii_strcasecmp (source, "server") == 0) {
01288 xmms_error_set (error, XMMS_ERROR_GENERIC,
01289 "Can't write to source server!");
01290 return;
01291 }
01292
01293 session = xmms_medialib_begin_write ();
01294 sourceid = xmms_medialib_source_to_id (session, source);
01295
01296 xmms_medialib_entry_property_set_str_source (session, entry, key, value,
01297 sourceid);
01298 xmms_medialib_end (session);
01299
01300 xmms_medialib_entry_send_update (entry);
01301 }
01302
01303 static void
01304 xmms_medialib_property_set_int_method (xmms_medialib_t *medialib, guint32 entry,
01305 const gchar *source, const gchar *key,
01306 gint32 value, xmms_error_t *error)
01307 {
01308 guint32 sourceid;
01309 xmms_medialib_session_t *session;
01310
01311 if (g_ascii_strcasecmp (source, "server") == 0) {
01312 xmms_error_set (error, XMMS_ERROR_GENERIC,
01313 "Can't write to source server!");
01314 return;
01315 }
01316
01317 session = xmms_medialib_begin_write ();
01318 sourceid = xmms_medialib_source_to_id (session, source);
01319 xmms_medialib_entry_property_set_int_source (session, entry, key, value,
01320 sourceid);
01321 xmms_medialib_end (session);
01322
01323 xmms_medialib_entry_send_update (entry);
01324 }
01325
01326 void
01327 xmms_medialib_property_remove (xmms_medialib_t *medialib, guint32 entry,
01328 const gchar *source, const gchar *key,
01329 xmms_error_t *error)
01330 {
01331 guint32 sourceid;
01332
01333 xmms_medialib_session_t *session = xmms_medialib_begin_write ();
01334 sourceid = xmms_medialib_source_to_id (session, source);
01335 xmms_sqlite_exec (session->sql,
01336 "delete from Media where source=%d and "
01337 "key='%s' and id=%d", sourceid, key, entry);
01338 xmms_medialib_end (session);
01339
01340 xmms_medialib_entry_send_update (entry);
01341 }
01342
01343 static void
01344 xmms_medialib_property_remove_method (xmms_medialib_t *medialib, guint32 entry,
01345 const gchar *source, const gchar *key,
01346 xmms_error_t *error)
01347 {
01348 if (g_ascii_strcasecmp (source, "server") == 0) {
01349 xmms_error_set (error, XMMS_ERROR_GENERIC,
01350 "Can't remove properties set by the server!");
01351 return;
01352 }
01353
01354 return xmms_medialib_property_remove (medialib, entry, source, key, error);
01355 }
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366 GList *
01367 xmms_medialib_select (xmms_medialib_session_t *session,
01368 const gchar *query, xmms_error_t *error)
01369 {
01370 GList *res = NULL;
01371 gint ret;
01372
01373 g_return_val_if_fail (query, 0);
01374 g_return_val_if_fail (session, 0);
01375
01376 ret = xmms_sqlite_query_table (session->sql, select_callback,
01377 (void *)&res, error, "%s", query);
01378
01379 return ret ? g_list_reverse (res) : NULL;
01380 }
01381
01382
01383
01384
01385
01386
01387
01388 gboolean
01389 xmms_medialib_check_id (xmms_medialib_entry_t entry)
01390 {
01391 xmms_medialib_session_t *session;
01392 gboolean ret;
01393
01394 session = xmms_medialib_begin ();
01395 ret = xmms_medialib_check_id_in_session (entry, session);
01396 xmms_medialib_end (session);
01397
01398 return ret;
01399 }
01400
01401
01402
01403
01404
01405 static gboolean
01406 xmms_medialib_check_id_in_session (xmms_medialib_entry_t entry,
01407 xmms_medialib_session_t *session)
01408 {
01409 gint c = 0;
01410
01411 if (!xmms_sqlite_query_int (session->sql, &c,
01412 "select count(id) from Media where id=%d",
01413 entry)) {
01414 return FALSE;
01415 }
01416
01417 return c > 0;
01418 }
01419
01420
01421
01422
01423
01424
01425
01426 xmms_medialib_entry_t
01427 xmms_medialib_entry_not_resolved_get (xmms_medialib_session_t *session)
01428 {
01429 gint32 ret = 0;
01430
01431 g_return_val_if_fail (session, 0);
01432
01433 xmms_sqlite_query_int (session->sql, &ret,
01434 "select id from Media where key='%s' "
01435 "and source=%d and value in (%d, %d) limit 1",
01436 XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS,
01437 XMMS_MEDIALIB_SOURCE_SERVER_ID,
01438 XMMS_MEDIALIB_ENTRY_STATUS_NEW,
01439 XMMS_MEDIALIB_ENTRY_STATUS_REHASH);
01440
01441 return ret;
01442 }
01443
01444 guint
01445 xmms_medialib_num_not_resolved (xmms_medialib_session_t *session)
01446 {
01447 gint ret;
01448 g_return_val_if_fail (session, 0);
01449
01450 xmms_sqlite_query_int (session->sql, &ret,
01451 "select count(id) as value from Media where "
01452 "key='%s' and value in (%d, %d) and source=%d",
01453 XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS,
01454 XMMS_MEDIALIB_ENTRY_STATUS_NEW,
01455 XMMS_MEDIALIB_ENTRY_STATUS_REHASH,
01456 XMMS_MEDIALIB_SOURCE_SERVER_ID);
01457
01458 return ret;
01459 }
01460
01461 gboolean
01462 xmms_medialib_decode_url (char *url)
01463 {
01464 int i = 0, j = 0;
01465
01466 g_return_val_if_fail (url, TRUE);
01467
01468 while (url[i]) {
01469 unsigned char chr = url[i++];
01470
01471 if (chr == '+') {
01472 url[j++] = ' ';
01473 } else if (chr == '%') {
01474 char ts[3];
01475 char *t;
01476
01477 ts[0] = url[i++];
01478 if (!ts[0])
01479 return FALSE;
01480 ts[1] = url[i++];
01481 if (!ts[1])
01482 return FALSE;
01483 ts[2] = '\0';
01484
01485 url[j++] = strtoul (ts, &t, 16);
01486 if (t != &ts[2])
01487 return FALSE;
01488 } else {
01489 url[j++] = chr;
01490 }
01491 }
01492
01493 url[j] = '\0';
01494
01495 return TRUE;
01496 }
01497
01498
01499 #define GOODCHAR(a) ((((a) >= 'a') && ((a) <= 'z')) || \
01500 (((a) >= 'A') && ((a) <= 'Z')) || \
01501 (((a) >= '0') && ((a) <= '9')) || \
01502 ((a) == ':') || \
01503 ((a) == '/') || \
01504 ((a) == '-') || \
01505 ((a) == '.') || \
01506 ((a) == '_'))
01507
01508
01509 gchar *
01510 xmms_medialib_url_encode (const gchar *path)
01511 {
01512 static gchar hex[16] = "0123456789abcdef";
01513 gchar *res;
01514 int i = 0, j = 0;
01515
01516 res = g_malloc (strlen (path) * 3 + 1);
01517 if (!res)
01518 return NULL;
01519
01520 while (path[i]) {
01521 guchar chr = path[i++];
01522 if (GOODCHAR (chr)) {
01523 res[j++] = chr;
01524 } else if (chr == ' ') {
01525 res[j++] = '+';
01526 } else {
01527 res[j++] = '%';
01528 res[j++] = hex[((chr & 0xf0) >> 4)];
01529 res[j++] = hex[(chr & 0x0f)];
01530 }
01531 }
01532
01533 res[j] = '\0';
01534
01535 return res;
01536 }