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