diff --git a/plugins/ipod/Makefile.am b/plugins/ipod/Makefile.am
index 82b1b08..1dc2505 100644
--- a/plugins/ipod/Makefile.am
+++ b/plugins/ipod/Makefile.am
@@ -2,6 +2,8 @@ plugindir = $(PLUGINDIR)/ipod
 plugin_LTLIBRARIES = libipod.la
 
 libipod_la_SOURCES = \
+	rb-ipod-db.c				\
+	rb-ipod-db.h				\
 	rb-ipod-plugin.c			\
 	rb-ipod-source.c			\
 	rb-ipod-source.h
diff --git a/plugins/ipod/rb-ipod-db.c b/plugins/ipod/rb-ipod-db.c
new file mode 100644
index 0000000..8f858a8
--- /dev/null
+++ b/plugins/ipod/rb-ipod-db.c
@@ -0,0 +1,786 @@
+/*
+ *  arch-tag: Abstraction of libgpod Itdb_ItunesDB object
+ *
+ *  Copyright (C) 2007 Christophe Fergeau  <teuf@gnome.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+#include <string.h>
+
+#include "rb-ipod-db.h"
+#include "rb-debug.h"
+
+/*
+ * This class is used to handle asynchronous saving of the iPod database 
+ * content. We must be really careful when doing that because libgpod API
+ * - and Itdb_iTunesDB in particular - isn't thread-safe at all.
+ * Thus this class was introduced in an attempt to wrap that complexity, and
+ * to hide it as much as possible from RbIpodSource.
+ * 
+ * When a save request for the iPod metadata is requested through 
+ * rb_ipod_db_save_async, we start by delaying the saving by a few seconds
+ * (using g_timeout_add) in case we'd get a bunch of very close save requests.
+ * When the timeout callback triggers, we start by marking the IpodDB object
+ * as read-only, ie while the async save is going on *NO MODIFICATIONS AT ALL
+ * MUST BE MADE TO THE ITDB_ITUNESDB OBJECT*. Once the IpodDB is marked as 
+ * read-only, we create a thread whose only purpose is to save the 
+ * Itdb_iTunesDB content, and once it has done its job, we go back to the main
+ * thread through a g_idle_add and mark the IpodDB object as read/write.
+ *
+ * Since the UI is not blocked during the async database saving (that's the 
+ * whole point of this exercise after all ;), we log all IpodDB modifications
+ * attempts in IpodDB::delayed_actions, and we'll replay them once the 
+ * async saving is done. When these IpodDB modifications should trigger changes
+ * in what RB displays (eg, it may be a song removal/addition from/to 
+ * the iPod), RbIpodSource should update the GUI immediatly even if the IpodDB
+ * hasn't been updated yet (actually, RbIpodSource shouldn't even know if
+ * the IpodDB changes it requested were delayed or not).
+ *
+ * The approach describes above could seem to nicely hide the Itdb_iTunesDB
+ * from RbIpodSource and prevent all unwanted modifications of the 
+ * Itdb_iTunesDB while it's being saved, but there's unfortunately a big
+ * gotcha: the Itdb_Track and Itdb_Playlist classes contains a pointer to the
+ * Itdb_iTunesDB they are attached to, and seemingly innocent libgpod functions
+ * can be called on those objects and modify the Itdb_iTunesDB through that 
+ * pointer without RbIpodDB knowing. As an example, itdb_track_remove is one 
+ * such functions. Consequently, one needs to be *really* careful when calling
+ * libgpod functions from RbIpodSource and needs to consider if this function
+ * should be wrapped in RbIpodDb (with the appropriate delayed handling) 
+ * instead of directly calling it from RbIpodSource
+ */
+
+typedef struct _RbIpodDelayedAction RbIpodDelayedAction;
+static void rb_ipod_free_delayed_action (RbIpodDelayedAction *action);
+static void rb_ipod_db_queue_remove_track (RbIpodDb *db,
+					   Itdb_Track *track);
+static void rb_ipod_db_queue_set_ipod_name (RbIpodDb *db, 
+					    const char *new_name);
+static void rb_ipod_db_queue_add_track (RbIpodDb *db, Itdb_Track *track);
+static void rb_ipod_db_queue_add_playlist (RbIpodDb *db, 
+					   Itdb_Playlist *playlist);
+static void rb_ipod_db_queue_add_to_playlist (RbIpodDb *ipod_db, 
+					      Itdb_Playlist *playlist,
+					      Itdb_Track *track);
+static void rb_ipod_db_queue_remove_from_playlist (RbIpodDb *ipod_db, 
+						   Itdb_Playlist *playlist,
+						   Itdb_Track *track);
+static void rb_ipod_db_queue_set_thumbnail (RbIpodDb *db,
+					    Itdb_Track *track,
+					    GdkPixbuf *pixbuf);
+static void rb_ipod_db_process_delayed_actions (RbIpodDb *ipod_db);
+
+typedef struct {
+	Itdb_iTunesDB *itdb;
+	gboolean needs_shuffle_db;
+
+	/* Read-only only applies to the iPod database, it's not an issue if
+	 * the track transfer code copies a new song to the iPod while it's set
+	 */
+	/* FIXME: if a track is copied to the iPod while this is set, and if 
+	 * the iPod goes away before we process the delayed actions and sync
+	 * the database, then the copied file will be leaked (ie hidden in the 
+	 * iPod directory tree but not accessible from the iPod UI)
+	 */
+	gboolean read_only;
+	GQueue *delayed_actions;
+	GThread *saving_thread;
+
+	guint save_timeout_id;
+	guint save_idle_id;
+
+} RbIpodDbPrivate;
+
+G_DEFINE_TYPE (RbIpodDb, rb_ipod_db, G_TYPE_OBJECT)
+
+#define IPOD_DB_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IPOD_DB, RbIpodDbPrivate))
+
+static void rb_itdb_save (RbIpodDb *ipod_db, GError **error)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	itdb_write (priv->itdb, error);
+	if ((error != NULL) && (*error != NULL)) {
+		return;
+	}
+	if (priv->needs_shuffle_db) {
+		itdb_shuffle_write (priv->itdb, error);
+	}
+}
+
+static void
+rb_ipod_db_init (RbIpodDb *db)
+
+{	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (db);
+
+	priv->delayed_actions = g_queue_new ();
+}
+
+static void 
+rb_ipod_db_dispose (GObject *object)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (object);
+	gboolean db_dirty = FALSE;
+
+	if (priv->saving_thread != NULL) {
+		g_thread_join (priv->saving_thread);
+		priv->saving_thread = NULL;
+	}
+
+	if (priv->save_idle_id != 0) {
+		g_source_remove (priv->save_idle_id);
+		priv->save_idle_id = 0;
+	}
+
+	/* Be careful, the order of the following cleanups is important, first
+	 * we process the queued ipod database modifications, which may trigger
+	 * modifications of save_timeout_id, then we make sure we cancel 
+	 * potential in progress saves, and finally we sync the database
+	 */
+	if (priv->delayed_actions) {
+		
+		if (g_queue_get_length (priv->delayed_actions) != 0) {
+			rb_ipod_db_process_delayed_actions (RB_IPOD_DB(object));
+			db_dirty = TRUE;
+		}
+		/* The queue should be empty, but better be safe than 
+		 * leaking 
+		 */
+		g_queue_foreach (priv->delayed_actions, 
+				 (GFunc)rb_ipod_free_delayed_action,
+				 NULL);
+		g_queue_free (priv->delayed_actions);
+		priv->delayed_actions = NULL;
+	}
+
+	if (priv->save_timeout_id != 0) {
+		g_source_remove (priv->save_timeout_id);
+		priv->save_timeout_id = 0;
+		db_dirty = TRUE;
+	}
+
+ 	if (priv->itdb != NULL) {
+		if (db_dirty) {
+			rb_itdb_save (RB_IPOD_DB (object), NULL);
+		}
+ 		itdb_free (priv->itdb);
+
+ 		priv->itdb = NULL;
+	}
+
+
+	G_OBJECT_CLASS (rb_ipod_db_parent_class)->dispose (object);
+}
+
+static void
+rb_ipod_db_class_init (RbIpodDbClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = rb_ipod_db_dispose;
+
+	g_type_class_add_private (klass, sizeof (RbIpodDbPrivate));
+}
+
+enum _RbIpodDelayedActionType {
+	RB_IPOD_ACTION_SET_NAME,
+	RB_IPOD_ACTION_ADD_TRACK,
+	RB_IPOD_ACTION_ADD_PLAYLIST,
+	RB_IPOD_ACTION_REMOVE_TRACK,
+	RB_IPOD_ACTION_SET_THUMBNAIL,
+	RB_IPOD_ACTION_ADD_TO_PLAYLIST,
+	RB_IPOD_ACTION_REMOVE_FROM_PLAYLIST
+};
+typedef enum _RbIpodDelayedActionType RbIpodDelayedActionType;
+
+struct _RbIpodDelayedSetThumbnail {
+	Itdb_Track *track;
+	GdkPixbuf *pixbuf;
+};
+typedef struct _RbIpodDelayedSetThumbnail RbIpodDelayedSetThumbnail;
+
+struct _RbIpodDelayedPlaylistOp {
+	Itdb_Playlist *playlist;
+	Itdb_Track *track;
+};
+typedef struct _RbIpodDelayedPlaylistOp RbIpodDelayedPlaylistOp;
+
+struct _RbIpodDelayedAction {
+	RbIpodDelayedActionType type;
+	union {
+		char *name;
+		Itdb_Playlist *playlist;
+		Itdb_Track *track;
+		RbIpodDelayedSetThumbnail thumbnail_data;
+		RbIpodDelayedPlaylistOp playlist_op;
+	};
+};
+
+static void rb_ipod_free_delayed_action (RbIpodDelayedAction *action) 
+{
+	switch (action->type) {
+	case RB_IPOD_ACTION_SET_NAME:
+		g_free (action->name);
+		break;
+	case RB_IPOD_ACTION_SET_THUMBNAIL:
+		g_object_unref (action->thumbnail_data.pixbuf);
+		break;
+	case RB_IPOD_ACTION_ADD_TRACK:
+		itdb_track_free (action->track);
+		break;
+	case RB_IPOD_ACTION_ADD_PLAYLIST:
+		itdb_playlist_free (action->playlist);
+		break;
+	case RB_IPOD_ACTION_REMOVE_TRACK:
+		/* Do nothing */
+		break;
+	case RB_IPOD_ACTION_ADD_TO_PLAYLIST:
+		/* Do nothing */
+		break;
+	case RB_IPOD_ACTION_REMOVE_FROM_PLAYLIST:
+		/* Do nothing */
+		break;
+	}
+	g_free (action);
+}
+
+const char *rb_ipod_db_get_ipod_name (RbIpodDb *db)
+{
+	Itdb_Playlist *mpl;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (db);
+	mpl = itdb_playlist_mpl (priv->itdb);
+	if (mpl == NULL) {
+		rb_debug ("Couldn't find iPod master playlist");
+		return NULL;
+	}
+
+	return mpl->name;	
+}
+
+static void
+rb_ipod_db_set_ipod_name_internal (RbIpodDb *ipod_db, const char *name)
+{
+	Itdb_Playlist *mpl;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	mpl = itdb_playlist_mpl (priv->itdb);
+	if (mpl != NULL) {
+		if (mpl->name != NULL) {
+			rb_debug ("Renaming iPod from %s to %s", mpl->name, name);
+			if (strcmp (mpl->name, name) == 0) {
+				rb_debug ("iPod is already named %s", name);
+				return;
+			}
+		}
+		g_free (mpl->name);
+		mpl->name = g_strdup (name);
+	} else {
+		g_warning ("iPod's master playlist is missing");
+	}
+
+	rb_ipod_db_save_async (ipod_db);
+}
+
+static void
+rb_ipod_db_remove_track_internal (RbIpodDb *ipod_db, Itdb_Track *track)
+{
+	GList *it;
+
+	for (it = track->itdb->playlists; it != NULL; it = it->next) {
+		itdb_playlist_remove_track ((Itdb_Playlist *)it->data, track);
+	}
+	itdb_track_remove (track);
+
+	rb_ipod_db_save_async (ipod_db);
+}
+
+static void
+rb_ipod_db_set_thumbnail_internal (RbIpodDb *ipod_db, Itdb_Track *track, 
+				   GdkPixbuf *pixbuf)
+{
+#ifdef HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF
+	itdb_track_set_thumbnails_from_pixbuf (track, pixbuf);
+#else /* HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF */
+	gchar *image_data;
+	gsize image_data_len;
+	GError *err = NULL;
+	gboolean success;
+
+	success = gdk_pixbuf_save_to_buffer (pixbuf,
+					     &image_data, &image_data_len,
+					     "jpeg", &err,
+					     "quality", "100",
+					     NULL);
+	if (!success) {
+		g_assert (image_data == NULL);
+		g_warning ("Failed to save pixbuf to buffer %s", err->message);
+		g_error_free (err);
+		return;
+	}
+
+	itdb_track_set_thumbnails_from_data (track, (guchar *) image_data, 
+					     image_data_len);
+	g_free (image_data);
+#endif /* HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF */
+
+	rb_ipod_db_save_async (ipod_db);
+}
+
+
+static void
+rb_ipod_db_add_playlist_internal (RbIpodDb *ipod_db, 
+				  Itdb_Playlist *playlist)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	itdb_playlist_add (priv->itdb, playlist, -1);
+
+	rb_ipod_db_save_async (ipod_db);
+}
+
+static void
+rb_ipod_db_add_track_internal (RbIpodDb *ipod_db, Itdb_Track *track)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	itdb_track_add (priv->itdb, track, -1);
+	itdb_playlist_add_track (itdb_playlist_mpl (priv->itdb),
+				 track, -1);
+
+	rb_ipod_db_save_async (ipod_db);
+}
+
+static void
+rb_ipod_db_add_to_playlist_internal (RbIpodDb* ipod_db,
+				     Itdb_Playlist *playlist,
+				     Itdb_Track *track)
+{
+	itdb_playlist_add_track (playlist, track, -1);
+	rb_ipod_db_save_async (ipod_db);
+}
+
+void rb_ipod_db_remove_from_playlist_internal (RbIpodDb* ipod_db, 
+					       Itdb_Playlist *playlist,
+					       Itdb_Track *track)
+{
+	itdb_playlist_remove_track (playlist, track);
+	rb_ipod_db_save_async (ipod_db);
+}
+
+
+void rb_ipod_db_set_thumbnail (RbIpodDb* ipod_db, Itdb_Track *track, 
+			       GdkPixbuf *pixbuf)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_set_thumbnail (ipod_db, track, pixbuf);
+	} else {
+		rb_ipod_db_set_thumbnail_internal (ipod_db, track, pixbuf);
+	}
+}
+
+void rb_ipod_db_add_track (RbIpodDb* ipod_db, Itdb_Track *track)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_add_track (ipod_db, track);
+	} else {
+		rb_ipod_db_add_track_internal (ipod_db, track);
+	}
+}
+
+void rb_ipod_db_add_playlist (RbIpodDb* ipod_db, Itdb_Playlist *playlist)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_add_playlist (ipod_db, playlist);
+	} else {
+		rb_ipod_db_add_playlist_internal (ipod_db, playlist);
+	}
+}
+
+void rb_ipod_db_remove_track (RbIpodDb* ipod_db, Itdb_Track *track)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_remove_track (ipod_db, track);
+	} else {
+		rb_ipod_db_remove_track_internal (ipod_db, track);
+	}
+}
+
+void rb_ipod_db_set_ipod_name (RbIpodDb *ipod_db, const char *name)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_set_ipod_name (ipod_db, name);
+	} else {
+		rb_ipod_db_set_ipod_name_internal (ipod_db, name);
+	}
+}
+
+void rb_ipod_db_add_to_playlist (RbIpodDb* ipod_db, Itdb_Playlist *playlist,
+				 Itdb_Track *track)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_add_to_playlist (ipod_db, playlist, track);
+	} else {
+		rb_ipod_db_add_to_playlist (ipod_db, playlist, track);
+	}	
+}
+
+void rb_ipod_db_remove_from_playlist (RbIpodDb* ipod_db, 
+				      Itdb_Playlist *playlist,
+				      Itdb_Track *track)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		rb_ipod_db_queue_remove_from_playlist (ipod_db, playlist,
+						       track);
+	} else {
+		rb_ipod_db_remove_from_playlist (ipod_db, playlist, track);
+	}	
+}
+
+
+static void 
+rb_ipod_db_process_delayed_actions (RbIpodDb *ipod_db)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	rb_debug ("Handling delayed iPod actions");
+
+	action = g_queue_pop_head (priv->delayed_actions);
+	if (action != NULL) {
+		/* Schedule a database save if there was at least one delayed
+		 * action
+		 */
+		rb_ipod_db_save_async (ipod_db);
+	}
+	while (action != NULL) {
+		switch (action->type) {
+		case RB_IPOD_ACTION_SET_NAME:
+			rb_debug ("IPOD_ACTION_SET_NAME (%s)",
+				  action->name);
+			rb_ipod_db_set_ipod_name_internal (ipod_db,
+							   action->name);
+			break;
+		case RB_IPOD_ACTION_SET_THUMBNAIL:
+			rb_debug ("IPOD_ACTION_SET_THUMBNAIL");
+			rb_ipod_db_set_thumbnail_internal (ipod_db,
+							   action->thumbnail_data.track,
+							   action->thumbnail_data.pixbuf);
+			break;
+		case RB_IPOD_ACTION_REMOVE_TRACK:
+			rb_debug ("IPOD_ACTION_REMOVE_TRACK");
+			rb_ipod_db_remove_track_internal (ipod_db,
+							  action->track);
+			break;
+		case RB_IPOD_ACTION_ADD_TRACK:
+			rb_debug ("IPOD_ACTION_ADD_TRACK");
+			rb_ipod_db_add_track_internal (ipod_db, action->track);
+			/* The track was added to the iPod database, 'action'
+			 * is no longer responsible for its memory handling
+			 */
+			action->track = NULL;
+			break;
+		case RB_IPOD_ACTION_ADD_PLAYLIST:
+			rb_debug ("IPOD_ACTION_ADD_PLAYLIST");
+			rb_ipod_db_add_playlist_internal (ipod_db, 
+							  action->playlist);
+			/* The playlist was added to the iPod database, 
+			 * 'action' is no longer responsible for its memory 
+			 * handling
+			 */
+			action->playlist = NULL;
+			break;
+		case RB_IPOD_ACTION_ADD_TO_PLAYLIST:
+			rb_debug ("IPOD_ACTION_ADD_TO_PLAYLIST");
+			rb_ipod_db_add_to_playlist_internal (ipod_db, 
+							     action->playlist_op.playlist,
+							     action->playlist_op.track);
+			break;
+		case RB_IPOD_ACTION_REMOVE_FROM_PLAYLIST:
+			rb_debug ("IPOD_ACTION_REMOVE_FROM_PLAYLIST");
+			rb_ipod_db_remove_from_playlist_internal (ipod_db, 
+								  action->playlist_op.playlist,
+								  action->playlist_op.track);
+			break;
+		}
+		rb_ipod_free_delayed_action (action);
+		action = g_queue_pop_head (priv->delayed_actions);
+	}
+}
+
+static void rb_ipod_db_queue_remove_track (RbIpodDb *ipod_db,
+					   Itdb_Track *track)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	
+	g_assert (priv->read_only);
+	rb_debug ("Queueing move to trash action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_REMOVE_TRACK;
+	action->track = track;
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static void rb_ipod_db_queue_set_ipod_name (RbIpodDb *ipod_db, 
+					    const char *new_name)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+		
+	g_assert (priv->read_only);
+	rb_debug ("Queueing set name action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_SET_NAME;
+	action->name = g_strdup (new_name);
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static void rb_ipod_db_queue_add_playlist (RbIpodDb *ipod_db,
+					   Itdb_Playlist *playlist)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	
+	g_assert (priv->read_only);
+	rb_debug ("Queueing add track action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_ADD_PLAYLIST;
+	action->playlist = playlist;
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static void rb_ipod_db_queue_add_track (RbIpodDb *ipod_db, Itdb_Track *track)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	
+	g_assert (priv->read_only);
+	rb_debug ("Queueing add track action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_ADD_TRACK;
+	action->track = track;
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static void rb_ipod_db_queue_add_to_playlist (RbIpodDb *ipod_db, 
+					      Itdb_Playlist *playlist,
+					      Itdb_Track *track)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	
+	g_assert (priv->read_only);
+	rb_debug ("Queueing add to playlist action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_ADD_TO_PLAYLIST;
+	action->playlist_op.playlist = playlist;
+	action->playlist_op.track = track;
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static void rb_ipod_db_queue_remove_from_playlist (RbIpodDb *ipod_db, 
+						   Itdb_Playlist *playlist,
+						   Itdb_Track *track)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	
+	g_assert (priv->read_only);
+	rb_debug ("Queueing remove from playlist action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_REMOVE_FROM_PLAYLIST;
+	action->playlist_op.playlist = playlist;
+	action->playlist_op.track = track;
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static void rb_ipod_db_queue_set_thumbnail (RbIpodDb *ipod_db,
+					    Itdb_Track *track,
+					    GdkPixbuf *pixbuf)
+{
+	RbIpodDelayedAction *action;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	
+	g_assert (priv->read_only);
+	rb_debug ("Queueing set thumbnail action since the iPod database is currently read-only");
+	action = g_new0 (RbIpodDelayedAction, 1);
+	action->type = RB_IPOD_ACTION_SET_THUMBNAIL;
+	action->thumbnail_data.track = track;
+	action->thumbnail_data.pixbuf = g_object_ref (G_OBJECT (track));
+	g_queue_push_tail (priv->delayed_actions, action);
+}
+
+static gchar *
+rb_ipod_db_get_volume_path (GnomeVFSVolume *volume)
+{
+	gchar *path;
+	gchar *uri;
+
+	uri = gnome_vfs_volume_get_activation_uri (volume);
+	path = g_filename_from_uri (uri, NULL, NULL);
+	g_assert (path != NULL);
+	g_free (uri);
+
+	return path;
+}
+
+static void rb_ipod_db_load (RbIpodDb *ipod_db, GnomeVFSVolume *volume)
+{
+	char *mount_path;
+	const Itdb_IpodInfo *info;
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	mount_path = rb_ipod_db_get_volume_path (volume);
+ 	priv->itdb = itdb_parse (mount_path, NULL);
+	g_free (mount_path);
+
+	info = itdb_device_get_ipod_info(priv->itdb->device);
+	if (info->ipod_generation == ITDB_IPOD_GENERATION_UNKNOWN ||
+	    info->ipod_model == ITDB_IPOD_MODEL_SHUFFLE) {
+		priv->needs_shuffle_db = TRUE;
+	} else {
+		priv->needs_shuffle_db = FALSE;
+	}
+}
+
+RbIpodDb *rb_ipod_db_new (GnomeVFSVolume *volume)
+{
+	RbIpodDb *db;
+
+	g_return_val_if_fail (volume != NULL, NULL);
+
+	db = g_object_new (RB_TYPE_IPOD_DB, NULL);
+	if (db == NULL) {
+		return NULL;
+	}
+
+	rb_ipod_db_load (db, volume);
+
+	return db;
+}
+
+
+/* Threaded iTunesDB save */
+static gboolean
+ipod_db_saved_idle_cb (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	g_assert (priv->read_only);
+	g_thread_join (priv->saving_thread);
+	priv->saving_thread = NULL;
+	priv->read_only = FALSE;
+	rb_debug ("Switching iPod database to read-write");
+
+	rb_ipod_db_process_delayed_actions (ipod_db);
+
+	priv->save_idle_id = 0;
+
+	rb_debug ("End of iPod database save");
+	return FALSE;
+}
+
+static gpointer saving_thread (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	g_assert (priv->read_only);
+
+	rb_debug ("Writing iPod database to disk");
+	rb_itdb_save (ipod_db, NULL);
+	priv->save_idle_id = g_idle_add ((GSourceFunc)ipod_db_saved_idle_cb, 
+					 ipod_db);
+	
+	return NULL;
+}
+
+static gboolean
+save_timeout_cb (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->read_only) {
+		g_warning ("Database is read-only, not saving");
+		return TRUE;
+	}
+	rb_debug ("Starting iPod database save");
+	rb_debug ("Switching iPod database to read-only");
+	priv->read_only = TRUE;
+	
+	priv->saving_thread = g_thread_create ((GThreadFunc)saving_thread,
+					       ipod_db, TRUE, NULL);
+	priv->save_timeout_id = 0;
+	return FALSE;
+}
+
+void
+rb_ipod_db_save_async (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	if (priv->save_timeout_id == 0) {
+		rb_debug ("Scheduling iPod database save in 15 seconds");
+		priv->save_timeout_id = g_timeout_add (15000, 
+						       (GSourceFunc)save_timeout_cb,
+						       ipod_db);
+	} else {
+		rb_debug ("Database save already scheduled");
+	}
+}
+
+GList *rb_ipod_db_get_playlists (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	return priv->itdb->playlists;
+}
+
+GList *rb_ipod_db_get_tracks (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	return priv->itdb->tracks;
+}
+
+const char *rb_ipod_db_get_mount_path (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+
+	return itdb_get_mountpoint (priv->itdb);
+}
+
+Itdb_Device *rb_ipod_db_get_device (RbIpodDb *ipod_db)
+{
+	RbIpodDbPrivate *priv = IPOD_DB_GET_PRIVATE (ipod_db);
+	if (priv->itdb == NULL) {
+		return NULL;
+	}
+
+	return priv->itdb->device;
+}
diff --git a/plugins/ipod/rb-ipod-db.h b/plugins/ipod/rb-ipod-db.h
new file mode 100644
index 0000000..9edf356
--- /dev/null
+++ b/plugins/ipod/rb-ipod-db.h
@@ -0,0 +1,71 @@
+/*
+ *  arch-tag: Abstraction of libgpod Itdb_ItunesDB object
+ *
+ *  Copyright (C) 2007 Christophe Fergeau  <teuf@gnome.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+#ifndef __RB_IPOD_DB_H
+#define __RB_IPOD_DB_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gpod/itdb.h>
+#include <libgnomevfs/gnome-vfs-volume.h>
+#include <libgnomevfs/gnome-vfs-volume-monitor.h>
+
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_IPOD_DB         (rb_ipod_db_get_type ())
+#define RB_IPOD_DB(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_IPOD_DB, RbIpodDb))
+#define RB_IPOD_DB_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_IPOD_DB, RbIpodDbClass))
+#define RB_IS_IPOD_DB(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_IPOD_DB))
+#define RB_IS_IPOD_DB_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_IPOD_DB))
+#define RB_IPOD_DB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_IPOD_DB, RbIpodDbClass))
+
+typedef struct 
+{
+	GObject parent;
+} RbIpodDb;
+
+typedef struct
+{
+	GObjectClass parent;
+} RbIpodDbClass;
+
+RbIpodDb *rb_ipod_db_new (GnomeVFSVolume *volume);
+GType rb_ipod_db_get_type (void);
+
+void rb_ipod_db_save_async (RbIpodDb *db);
+
+void rb_ipod_db_set_thumbnail (RbIpodDb* ipod_db, Itdb_Track *track, 
+			       GdkPixbuf *pixbuf);
+void rb_ipod_db_add_track (RbIpodDb* ipod_db, Itdb_Track *track);
+void rb_ipod_db_add_playlist (RbIpodDb* ipod_db, Itdb_Playlist *playlist);
+void rb_ipod_db_add_to_playlist (RbIpodDb* ipod_db, Itdb_Playlist *playlist,
+				 Itdb_Track *track);
+void rb_ipod_db_remove_from_playlist (RbIpodDb* ipod_db, 
+				      Itdb_Playlist *playlist,
+				      Itdb_Track *track);
+void rb_ipod_db_remove_track (RbIpodDb* ipod_db, Itdb_Track *track);
+void rb_ipod_db_set_ipod_name (RbIpodDb *db, const char *name);
+const char *rb_ipod_db_get_ipod_name (RbIpodDb *db);
+
+GList *rb_ipod_db_get_playlists (RbIpodDb *ipod_db);
+GList *rb_ipod_db_get_tracks (RbIpodDb *ipod_db);
+const char *rb_ipod_db_get_mount_path (RbIpodDb *ipod_db);
+Itdb_Device *rb_ipod_db_get_device (RbIpodDb *ipod_db);
+#endif
diff --git a/plugins/ipod/rb-ipod-source.c b/plugins/ipod/rb-ipod-source.c
index b119398..a70bf2e 100644
--- a/plugins/ipod/rb-ipod-source.c
+++ b/plugins/ipod/rb-ipod-source.c
@@ -1,7 +1,7 @@
 /*
  *  arch-tag: Implementation of ipod source object
  *
- *  Copyright (C) 2004 Christophe Fergeau  <teuf@gnome.org>
+ *  Copyright (C) 2004, 2007 Christophe Fergeau  <teuf@gnome.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@
 
 #include "eel-gconf-extensions.h"
 #include "rb-ipod-source.h"
+#include "rb-ipod-db.h"
 #include "rb-debug.h"
 #include "rb-file-helpers.h"
 #include "rb-plugin.h"
@@ -57,7 +58,6 @@ static void rb_ipod_source_dispose (GObject *object);
 static gboolean impl_show_popup (RBSource *source);
 static void impl_move_to_trash (RBSource *asource);
 static void rb_ipod_load_songs (RBiPodSource *source);
-static gchar *rb_ipod_get_mount_path (GnomeVFSVolume *volume);
 static void impl_delete_thyself (RBSource *source);
 static GList* impl_get_ui_actions (RBSource *source);
 #ifdef HAVE_HAL
@@ -81,13 +81,11 @@ static gchar* ipod_get_filename_for_uri (const gchar *mount_point,
 static gchar* ipod_path_from_unix_path (const gchar *mount_point,
 					const gchar *unix_path);
 #endif
-static void itdb_schedule_save (RBiPodSource *source);
 static RhythmDB *get_db_for_source (RBiPodSource *source);
 
 typedef struct
 {
-	Itdb_iTunesDB *ipod_db;
-	gchar *ipod_mount_path;
+	RbIpodDb *ipod_db;
 	GHashTable *entry_map;
 
 	gboolean needs_shuffle_db;
@@ -135,27 +133,13 @@ rb_ipod_source_class_init (RBiPodSourceClass *klass)
 	g_type_class_add_private (klass, sizeof (RBiPodSourcePrivate));
 }
 
+
 static void
 rb_ipod_source_set_ipod_name (RBiPodSource *source, const char *name)
 {
-	Itdb_Playlist *mpl;
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 
-	mpl = itdb_playlist_mpl (priv->ipod_db);
-	if (mpl != NULL) {
-		if (mpl->name != NULL) {
-			rb_debug ("Renaming iPod from %s to %s", mpl->name, name);
-			if (strcmp (mpl->name, name) == 0) {
-				rb_debug ("iPod is already named %s", name);
-				return;
-			}
-		}
-		g_free (mpl->name);
-		mpl->name = g_strdup (name);
-		itdb_schedule_save (source);
-	} else {
-		g_warning ("iPod's master playlist is missing");
-	}
+	rb_ipod_db_set_ipod_name (priv->ipod_db, name);
 }
 
 static void
@@ -171,7 +155,7 @@ rb_ipod_source_name_changed_cb (RBiPodSource *source, GParamSpec *spec,
 
 static void
 rb_ipod_source_init (RBiPodSource *source)
-{
+{	
 	g_signal_connect (G_OBJECT (source), "notify::name",
 			  (GCallback)rb_ipod_source_name_changed_cb, NULL);
 }
@@ -182,12 +166,9 @@ rb_ipod_source_constructor (GType type, guint n_construct_properties,
 {
 	RBiPodSource *source;
 	RBEntryView *songs;
-	RBiPodSourcePrivate *priv;
 
 	source = RB_IPOD_SOURCE (G_OBJECT_CLASS (rb_ipod_source_parent_class)->
 			constructor (type, n_construct_properties, construct_properties));
-	priv = IPOD_SOURCE_GET_PRIVATE (source);
-
 	songs = rb_source_get_entry_view (RB_SOURCE (source));
 	rb_entry_view_append_column (songs, RB_ENTRY_VIEW_COL_RATING, FALSE);
 	rb_entry_view_append_column (songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
@@ -202,14 +183,9 @@ rb_ipod_source_dispose (GObject *object)
 {
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (object);
 
- 	if (priv->ipod_db != NULL) {
- 		itdb_free (priv->ipod_db);
- 		priv->ipod_db = NULL;
-	}
-
-	if (priv->ipod_mount_path) {
-		g_free (priv->ipod_mount_path);
-		priv->ipod_mount_path = NULL;
+	if (priv->ipod_db) {
+		g_object_unref (G_OBJECT (priv->ipod_db));
+		priv->ipod_db = NULL;
 	}
 
 	if (priv->entry_map) {
@@ -314,9 +290,7 @@ playlist_track_removed (RhythmDBQueryModel *m,
 	Itdb_Track *track;
         track = g_hash_table_lookup (priv->entry_map, entry);
 	g_return_if_fail (track != NULL);
-
-	itdb_playlist_remove_track (ipod_pl, track);
-	itdb_schedule_save (ipod);
+	rb_ipod_db_remove_from_playlist (priv->ipod_db, ipod_pl, track);
 }
 
 static void
@@ -334,8 +308,7 @@ playlist_track_added (GtkTreeModel *model, GtkTreePath *path,
         track = g_hash_table_lookup (priv->entry_map, entry);
 	g_return_if_fail (track != NULL);
 
-	itdb_playlist_add_track (ipod_pl, track, -1);
-	itdb_schedule_save (ipod);
+	rb_ipod_db_add_to_playlist (priv->ipod_db, ipod_pl, track);
 }
 
 static void
@@ -362,10 +335,11 @@ add_rb_playlist (RBiPodSource *source, Itdb_Playlist *playlist)
 	for (it = playlist->members; it != NULL; it = it->next) {
 		Itdb_Track *song;
 		char *filename;
+		const char *mount_path;
 
 		song = (Itdb_Track *)it->data;
- 		filename = ipod_path_to_uri (priv->ipod_mount_path,
- 					    song->ipod_path);
+		mount_path = rb_ipod_db_get_mount_path (priv->ipod_db);
+ 		filename = ipod_path_to_uri (mount_path, song->ipod_path);
 		rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (playlist_source),
 							filename, -1);
 		g_free (filename);
@@ -400,7 +374,9 @@ load_ipod_playlists (RBiPodSource *source)
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	GList *it;
 
-	for (it = priv->ipod_db->playlists; it != NULL; it = it->next) {
+	for (it = rb_ipod_db_get_playlists (priv->ipod_db);
+	     it != NULL;
+	     it = it->next) {
 		Itdb_Playlist *playlist;
 
 		playlist = (Itdb_Playlist *)it->data;
@@ -465,13 +441,13 @@ add_ipod_song_to_db (RBiPodSource *source, RhythmDB *db, Itdb_Track *song)
 	RhythmDBEntryType entry_type;
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	char *pc_path;
+	const char *mount_path;
 
 	/* Set URI */
 	g_object_get (source, "entry-type", &entry_type,
 		      NULL);
-
-	pc_path = ipod_path_to_uri (priv->ipod_mount_path,
-				    song->ipod_path);
+	mount_path = rb_ipod_db_get_mount_path (priv->ipod_db);
+	pc_path = ipod_path_to_uri (mount_path, song->ipod_path);
 	entry = rhythmdb_entry_new (RHYTHMDB (db), entry_type,
 				    pc_path);
 	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
@@ -639,7 +615,9 @@ load_ipod_db_idle_cb (RBiPodSource *source)
 	db = get_db_for_source (source);
 
 	g_assert (db != NULL);
- 	for (it = priv->ipod_db->tracks; it != NULL; it = it->next) {
+ 	for (it = rb_ipod_db_get_tracks (priv->ipod_db);
+	     it != NULL;
+	     it = it->next) {
 		add_ipod_song_to_db (source, db, (Itdb_Track *)it->data);
 	}
 
@@ -657,32 +635,17 @@ rb_ipod_load_songs (RBiPodSource *source)
 {
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	GnomeVFSVolume *volume;
-	const Itdb_IpodInfo *info;
 
 	g_object_get (source, "volume", &volume, NULL);
-	priv->ipod_mount_path = rb_ipod_get_mount_path (volume);
-
- 	priv->ipod_db = itdb_parse (priv->ipod_mount_path, NULL);
-
-	info = itdb_device_get_ipod_info(priv->ipod_db->device);
-	if (info->ipod_generation == ITDB_IPOD_GENERATION_UNKNOWN ||
-	    info->ipod_model == ITDB_IPOD_MODEL_SHUFFLE) {
-		priv->needs_shuffle_db = TRUE;
-	} else {
-		priv->needs_shuffle_db = FALSE;
-	}
-
+ 	priv->ipod_db = rb_ipod_db_new (volume);
 	priv->entry_map = g_hash_table_new (g_direct_hash, g_direct_equal);
-	if ((priv->ipod_db != NULL) && (priv->entry_map != NULL)) {
-		Itdb_Playlist *mpl;
 
-		/* FIXME: we could set a different icon depending on the iPod
-		 * model
-		 */
-		mpl = itdb_playlist_mpl (priv->ipod_db);
-		if (mpl && mpl->name) {
+	if ((priv->ipod_db != NULL) && (priv->entry_map != NULL)) {
+		const char *name;
+		name = rb_ipod_db_get_ipod_name (priv->ipod_db);
+		if (name) {
 			g_object_set (RB_SOURCE (source),
-				      "name", mpl->name,
+				      "name", name,
 				      NULL);
 		}
 		priv->load_idle_id = g_idle_add ((GSourceFunc)load_ipod_db_idle_cb, source);
@@ -692,20 +655,6 @@ rb_ipod_load_songs (RBiPodSource *source)
 }
 
 static gchar *
-rb_ipod_get_mount_path (GnomeVFSVolume *volume)
-{
-	gchar *path;
-	gchar *uri;
-
-	uri = gnome_vfs_volume_get_activation_uri (volume);
-	path = g_filename_from_uri (uri, NULL, NULL);
-	g_assert (path != NULL);
-	g_free (uri);
-
-	return path;
-}
-
-static gchar *
 rb_ipod_get_itunesdb_path (GnomeVFSVolume *volume)
 {
 	gchar *mount_point_uri;
@@ -862,17 +811,6 @@ impl_show_popup (RBSource *source)
 }
 
 static void
-remove_track_from_db (Itdb_Track *track)
-{
-	GList *it;
-
-	for (it = track->itdb->playlists; it != NULL; it = it->next) {
-		itdb_playlist_remove_track ((Itdb_Playlist *)it->data, track);
-	}
-	itdb_track_remove (track);
-}
-
-static void
 impl_move_to_trash (RBSource *asource)
 {
 	GList *sel, *tem;
@@ -899,40 +837,17 @@ impl_move_to_trash (RBSource *asource)
 			continue;
 		}
 
- 		remove_track_from_db (track);
+		rb_ipod_db_remove_track (priv->ipod_db, track);
 		g_hash_table_remove (priv->entry_map, entry);
 		rhythmdb_entry_move_to_trash (db, entry);
 		rhythmdb_commit (db);
 	}
 
-	if (sel != NULL) {
-		itdb_schedule_save (RB_IPOD_SOURCE (asource));
-	}
-
 	g_object_unref (db);
 
 	g_list_free (sel);
 }
 
-static void
-itdb_schedule_save (RBiPodSource *source)
-{
-	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
-
-	/* FIXME: should probably be delayed a bit to avoid doing
-	 * it after each file when we are copying several files
-	 * consecutively
-	 * FIXME: or this function could be called itdb_set_dirty, and we'd
-	 * have a timeout firing every 5 seconds and saving the db if it's
-	 * dirty
-	 */
-	itdb_write (priv->ipod_db, NULL);
-
-	if (priv->needs_shuffle_db) {
-		itdb_shuffle_write (priv->ipod_db, NULL);
-	}
-}
-
 #ifdef ENABLE_IPOD_WRITING
 static char *
 impl_build_dest_uri (RBRemovableMediaSource *source,
@@ -943,9 +858,12 @@ impl_build_dest_uri (RBRemovableMediaSource *source,
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	const char *uri;
 	char *dest;
+	const char *mount_path;
 
 	uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
-	dest = ipod_get_filename_for_uri (priv->ipod_mount_path,  uri, mimetype, extension);
+	mount_path = rb_ipod_db_get_mount_path (priv->ipod_db);
+	dest = ipod_get_filename_for_uri (mount_path,  uri, 
+					  mimetype, extension);
 	if (dest != NULL) {
 		char *dest_uri;
 
@@ -966,39 +884,17 @@ artwork_notify_cb (RhythmDB *db,
 {
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (isource);
 	Itdb_Track *song;
-#ifdef HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF
-#else /* HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF */
-	gchar *image_data;
-	gsize image_data_len;
-	GError *err = NULL;
-	gboolean success;
-#endif /* HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF */
-	
-	song = g_hash_table_lookup (priv->artwork_request_map, entry);
-	if (song == NULL)
-		return;
+	GdkPixbuf *pixbuf;
 
 	g_return_if_fail (G_VALUE_HOLDS (metadata, GDK_TYPE_PIXBUF));
-#ifdef HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF
-	itdb_track_set_thumbnails_from_pixbuf (song, g_value_get_object (metadata));
-#else /* HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF */
-	success = gdk_pixbuf_save_to_buffer (GDK_PIXBUF (g_value_get_object (metadata)),
-					     &image_data, &image_data_len,
-					     "jpeg", &err,
-					     "quality", "100",
-					     NULL);
-	if (!success) {
-		g_assert (image_data == NULL);
-		g_warning ("Failed to save pixbuf to buffer %s", err->message);
-		g_error_free (err);
+	pixbuf = GDK_PIXBUF (g_value_get_object (metadata));
+		
+	song = g_hash_table_lookup (priv->artwork_request_map, entry);
+	if (song == NULL)
 		return;
-	}
 
+	rb_ipod_db_set_thumbnail (priv->ipod_db, song, pixbuf);
 	g_hash_table_remove (priv->artwork_request_map, entry);
-	itdb_track_set_thumbnails_from_data (song, (guchar *) image_data, image_data_len);
-	g_free (image_data);
-#endif /* HAVE_ITDB_TRACK_SET_THUMBNAILS_FROM_PIXBUF */
-	itdb_schedule_save (isource);
 }
 
 static void
@@ -1032,17 +928,19 @@ add_to_podcasts (RBiPodSource *source, Itdb_Track *song)
 {
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	gchar *filename;
+	const gchar *mount_path;
 
 	if (priv->podcast_pl == NULL) {
 		/* No Podcast playlist on the iPod, create a new one */
 		Itdb_Playlist *ipod_playlist;
 		ipod_playlist = itdb_playlist_new (_("Podcasts"), FALSE);
 		itdb_playlist_set_podcasts (ipod_playlist);
-		itdb_playlist_add (priv->ipod_db, ipod_playlist, -1);
+		rb_ipod_db_add_playlist (priv->ipod_db, ipod_playlist);
 		add_rb_playlist (source, ipod_playlist);
 	}
 
-  	filename = ipod_path_to_uri (priv->ipod_mount_path, song->ipod_path);
+	mount_path = rb_ipod_db_get_mount_path (priv->ipod_db);
+  	filename = ipod_path_to_uri (mount_path, song->ipod_path);
  	rb_static_playlist_source_add_location (priv->podcast_pl, filename, -1);
 	g_free (filename);
 }
@@ -1063,13 +961,14 @@ impl_track_added (RBRemovableMediaSource *source,
 	if (song != NULL) {
 		RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 		char *filename;
+		const char *mount_path;
 
 		filename = g_filename_from_uri (dest, NULL, NULL);
-		song->ipod_path = ipod_path_from_unix_path (priv->ipod_mount_path, filename);
+		mount_path = rb_ipod_db_get_mount_path (priv->ipod_db);
+		song->ipod_path = ipod_path_from_unix_path (mount_path,
+							    filename);
 		g_free (filename);
-		itdb_track_add (priv->ipod_db, song, -1);
-		itdb_playlist_add_track (itdb_playlist_mpl (priv->ipod_db),
-					 song, -1);
+
 		if (song->mediatype == MEDIATYPE_PODCAST) {
 			add_to_podcasts (isource, song);
 		}
@@ -1077,14 +976,16 @@ impl_track_added (RBRemovableMediaSource *source,
 		/* reuse that #define since both functions were added to 
 		 * libgpod CVS HEAD around the same time
 		 */
-		if (itdb_device_supports_artwork (priv->ipod_db->device)) {
+		Itdb_Device *device;
+		device = rb_ipod_db_get_device (priv->ipod_db);		
+		if (device && itdb_device_supports_artwork (device)) {
 			request_artwork (isource, entry, db, song);
 		}
 #else 
 		request_artwork (isource, entry, db, song);
 #endif
 		add_ipod_song_to_db (isource, db, song);
-		itdb_schedule_save (isource);
+		rb_ipod_db_add_track (priv->ipod_db, song);
 	}
 
 	g_object_unref (db);
@@ -1328,7 +1229,9 @@ impl_delete_thyself (RBSource *source)
 	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	GList *p;
 
-	for (p = priv->ipod_db->playlists; p != NULL; p = p->next) {
+	for (p = rb_ipod_db_get_playlists (priv->ipod_db);
+	     p != NULL;
+	     p = p->next) {
 		Itdb_Playlist *playlist = (Itdb_Playlist *)p->data;
 		if (!itdb_playlist_is_mpl (playlist)) {
 			RBSource *rb_playlist = RB_SOURCE (playlist->userdata);
@@ -1336,8 +1239,10 @@ impl_delete_thyself (RBSource *source)
 		}
 	}
 
-	itdb_free (priv->ipod_db);
-	priv->ipod_db = NULL;
+	if (priv->ipod_db != NULL) {
+		g_object_unref (G_OBJECT (priv->ipod_db));
+		priv->ipod_db = NULL;
+	}
 
 	RB_SOURCE_CLASS (rb_ipod_source_parent_class)->impl_delete_thyself (source);
 }
