diff -rN -u old-pie2/backends/gstreamer/rb-encoder-gst.c new-pie2/backends/gstreamer/rb-encoder-gst.c
--- old-pie2/backends/gstreamer/rb-encoder-gst.c	2006-08-13 16:40:31.000000000 +1000
+++ new-pie2/backends/gstreamer/rb-encoder-gst.c	2006-08-13 16:40:32.000000000 +1000
@@ -67,7 +67,8 @@
 	gboolean error_emitted;
 	gboolean completion_emitted;
 
-	gulong total_length;
+	GstFormat position_format;
+	guint64 total_length;
 	guint progress_id;
 };
 
@@ -247,20 +248,21 @@
 static gboolean
 progress_timeout_cb (RBEncoderGst *encoder)
 {
-	gint64 nanos;
-	gint secs;
-	static GstFormat format = GST_FORMAT_TIME;
+	gint64 position;
+	static GstFormat format;
 	GstState state;
 
 	if (encoder->priv->pipeline == NULL)
 		return FALSE;
 
+	format = encoder->priv->position_format;
+
 #ifdef HAVE_GSTREAMER_0_10
 	gst_element_get_state (encoder->priv->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
 	if (state != GST_STATE_PLAYING)
 		return FALSE;
 
-	if (!gst_element_query_position (encoder->priv->pipeline, &format, &nanos)) {
+	if (!gst_element_query_position (encoder->priv->pipeline, &format, &position)) {
 		g_warning ("Could not get current track position");
 		return TRUE;
 	}
@@ -269,15 +271,20 @@
 	if (state != GST_STATE_PLAYING)
 		return FALSE;
 
-	if (!gst_element_query (encoder->priv->pipeline, GST_QUERY_POSITION, &format, &nanos)) {
+	if (!gst_element_query (encoder->priv->pipeline, GST_QUERY_POSITION, &format, &position)) {
 		g_warning ("Could not get current track position");
 		return TRUE;
 	}
 #endif
 
-	secs = nanos / GST_SECOND;
-	rb_debug ("encoding progress at %d out of %lu", secs, encoder->priv->total_length);
-	_rb_encoder_emit_progress (RB_ENCODER (encoder), ((double)secs) / encoder->priv->total_length);
+	if (format == GST_FORMAT_TIME) {
+		gint secs = position / GST_SECOND;
+		rb_debug ("encoding progress at %d out of %lld", secs, encoder->priv->total_length);
+		_rb_encoder_emit_progress (RB_ENCODER (encoder), ((double)secs) / encoder->priv->total_length);
+	} else {
+		rb_debug ("encoding progress at %lld out of %lld", position, encoder->priv->total_length);
+		_rb_encoder_emit_progress (RB_ENCODER (encoder), ((double)position) / encoder->priv->total_length);
+	}
 
 	return TRUE;
 }
@@ -939,18 +946,21 @@
 	entry_mime_type = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);
 	was_raw = g_str_has_prefix (entry_mime_type, "audio/x-raw");
 
-	priv->total_length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
 
 	create_parent_dirs (dest);
 
 	if ((mime_type == NULL && !was_raw) || (mime_type && (strcmp (mime_type, entry_mime_type) == 0))) {
 		result = copy_track (RB_ENCODER_GST (encoder), entry, dest, &error);
+		priv->total_length = rhythmdb_entry_get_uint64 (entry, RHYTHMDB_PROP_FILE_SIZE);
+		priv->position_format = GST_FORMAT_BYTES;
 	} else {
 		if (mime_type == NULL) {
 			result = extract_track (RB_ENCODER_GST (encoder), entry, dest, &error);
 		} else {
 			result = transcode_track (RB_ENCODER_GST (encoder), entry, dest, mime_type, &error);
 		}
+		priv->total_length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
+		priv->position_format = GST_FORMAT_TIME;
 	}
 
 	if (error) {

diff -rN -u old-pie2/lib/rb-util.c new-pie2/lib/rb-util.c
--- old-pie2/lib/rb-util.c	2006-08-13 16:40:31.000000000 +1000
+++ new-pie2/lib/rb-util.c	2006-08-13 16:40:32.000000000 +1000
@@ -858,3 +858,19 @@
 	g_free (val);
 }
 
+gboolean
+rb_str_in_strv (const char *needle, char **haystack)
+{
+	int i;
+
+	if (needle == NULL || haystack == NULL)
+		return FALSE;
+
+	for (i = 0; haystack[i] != NULL; i++) {
+		if (strcmp (needle, haystack[i]) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
diff -rN -u old-pie2/lib/rb-util.h new-pie2/lib/rb-util.h
--- old-pie2/lib/rb-util.h	2006-08-13 16:40:31.000000000 +1000
+++ new-pie2/lib/rb-util.h	2006-08-13 16:40:32.000000000 +1000
@@ -58,6 +58,8 @@
 void rb_list_deep_free (GList *list);
 GList* rb_string_list_copy (GList *list);
 
+gboolean rb_str_in_strv (const char *needle, char **haystack);
+
 GList* rb_collate_hash_table_keys (GHashTable *table);
 GList* rb_collate_hash_table_values (GHashTable *table);
 

diff -rN -u old-pie2/plugins/generic-player/rb-generic-player-source.c new-pie2/plugins/generic-player/rb-generic-player-source.c
--- old-pie2/plugins/generic-player/rb-generic-player-source.c	2006-08-13 16:40:32.000000000 +1000
+++ new-pie2/plugins/generic-player/rb-generic-player-source.c	2006-08-13 16:40:32.000000000 +1000
@@ -40,6 +40,7 @@
 #include "eel-gconf-extensions.h"
 #include "rb-static-playlist-source.h"
 #include "rb-generic-player-source.h"
+#include "rb-removable-media-manager.h"
 #include "rb-debug.h"
 #include "rb-util.h"
 #include "rb-file-helpers.h"
@@ -59,6 +60,9 @@
 static gboolean impl_show_popup (RBSource *source);
 static void impl_delete_thyself (RBSource *source);
 static gboolean impl_can_move_to_trash (RBSource *source);
+static gboolean impl_can_paste (RBSource *source);
+static char* impl_build_dest_uri (RBRemovableMediaSource *source,
+					RhythmDBEntry *entry);
 
 static gchar *default_get_mount_path (RBGenericPlayerSource *source);
 static void default_load_playlists (RBGenericPlayerSource *source);
@@ -86,6 +90,7 @@
 
 	/* information derived from HAL */
 	char **audio_folders;
+	char **output_mime_types;
 	gboolean playlist_format_unknown;
 	gboolean playlist_format_pls;
 	gboolean playlist_format_m3u;
@@ -103,6 +108,7 @@
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+	RBRemovableMediaSourceClass *rms_class = RB_REMOVABLE_MEDIA_SOURCE_CLASS (klass);
 
 	object_class->constructor = rb_generic_player_source_constructor;
 	object_class->dispose = rb_generic_player_source_dispose;
@@ -111,6 +117,9 @@
 	source_class->impl_show_popup = impl_show_popup;
 	source_class->impl_delete_thyself = impl_delete_thyself;
 	source_class->impl_can_move_to_trash = impl_can_move_to_trash;
+	source_class->impl_can_paste = impl_can_paste;
+
+	rms_class->impl_build_dest_uri = impl_build_dest_uri;
 
 	klass->impl_get_mount_path = default_get_mount_path;
 	klass->impl_load_playlists = default_load_playlists;
@@ -129,7 +138,7 @@
 rb_generic_player_source_constructor (GType type, guint n_construct_properties,
 			       GObjectConstructParam *construct_properties)
 {
-	RBGenericPlayerSource *source; 
+	RBGenericPlayerSource *source;
 	RBGenericPlayerSourcePrivate *priv;
 	GnomeVFSVolume *volume;
 	RBShell *shell;
@@ -153,7 +162,7 @@
 	priv->playlist_format_unknown = TRUE;
 
 	rb_generic_player_source_load_songs (source);
-	
+
 	g_idle_add ((GSourceFunc)rb_generic_player_source_load_playlists, source);
 
 	return G_OBJECT (source);
@@ -166,7 +175,7 @@
 	GnomeVFSVolume *volume;
 	LibHalContext *ctx = get_hal_context ();
 	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
-	
+
 	if (ctx) {
 		gchar *udi;
 
@@ -183,34 +192,55 @@
 			/* get audio folders */
 			dbus_error_init (&error);
 			proplist = libhal_device_get_property_strlist (ctx, udi, "portable_audio_player.audio_folders", &error);
-			if (proplist && !dbus_error_is_set (&error)) {
-				char *dbg;
-
-				priv->audio_folders = g_strdupv (proplist);
+			if (proplist) {
+				if (!dbus_error_is_set (&error)) {
+					char *dbg;
+
+					priv->audio_folders = g_strdupv (proplist);
+
+					dbg = g_strjoinv(", ", priv->audio_folders);
+					rb_debug ("got audio player folder list: %s", dbg);
+					g_free (dbg);
+				}
 				libhal_free_string_array (proplist);
-
-				dbg = g_strjoinv(", ", priv->audio_folders);
-				rb_debug ("got audio player folder list: %s", dbg);
-				g_free (dbg);
 			}
 			free_dbus_error ("getting audio folder list", &error);
 
+			/* get supported mime-types */
+			dbus_error_init (&error);
+			proplist = libhal_device_get_property_strlist (ctx, udi, "portable_audio_player.output_formats", &error);
+			if (proplist) {
+				if (!dbus_error_is_set (&error)) {
+					char *dbg;
+
+					priv->output_mime_types = g_strdupv (proplist);
+
+					dbg = g_strjoinv(", ", priv->output_mime_types);
+					rb_debug ("got output mime-type list: %s", dbg);
+					g_free (dbg);
+				}
+				libhal_free_string_array (proplist);
+			}
+			free_dbus_error ("getting supported mime-type list", &error);
+
 			/* get playlist format */
 			dbus_error_init (&error);
 			proplist = libhal_device_get_property_strlist (ctx, udi, "portable_audio_player.playlist_format", &error);
-			if (proplist && !dbus_error_is_set (&error)) {
-				int fmt;
-				for (fmt = 0; proplist[fmt] != NULL; fmt++) {
-					if (strcmp (proplist[fmt], "audio/x-mpegurl") == 0) {
-						rb_debug ("device supports M3U playlists");
-						priv->playlist_format_unknown = FALSE;
-						priv->playlist_format_m3u = TRUE;
-					} else if (strcmp (proplist[fmt], "audio/x-scpls") == 0) {
-						rb_debug ("device supports PLS playlists");
-						priv->playlist_format_unknown = FALSE;
-						priv->playlist_format_pls = TRUE;
-					} else {
-						rb_debug ("unrecognized playlist format: %s", proplist[fmt]);
+			if (proplist) {
+				if (!dbus_error_is_set (&error)) {
+					int fmt;
+					for (fmt = 0; proplist[fmt] != NULL; fmt++) {
+						if (strcmp (proplist[fmt], "audio/x-mpegurl") == 0) {
+							rb_debug ("device supports M3U playlists");
+							priv->playlist_format_unknown = FALSE;
+							priv->playlist_format_m3u = TRUE;
+						} else if (strcmp (proplist[fmt], "audio/x-scpls") == 0) {
+							rb_debug ("device supports PLS playlists");
+							priv->playlist_format_unknown = FALSE;
+							priv->playlist_format_pls = TRUE;
+						} else {
+							rb_debug ("unrecognized playlist format: %s", proplist[fmt]);
+						}
 					}
 				}
 
@@ -275,6 +305,7 @@
 
 	g_free (priv->mount_path);
 	g_strfreev (priv->audio_folders);
+	g_strfreev (priv->output_mime_types);
 	g_free (priv->playlist_path);
 }
 
@@ -309,7 +340,7 @@
 {
 	GList *p;
 	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
-	
+
 	for (p = priv->playlists; p != NULL; p = p->next) {
 		RBSource *playlist = RB_SOURCE (p->data);
 		rb_source_delete_thyself (playlist);
@@ -329,7 +360,7 @@
 	priv->mount_path = rb_generic_player_source_get_mount_path (source);
 	g_object_get (G_OBJECT (source), "entry-type", &entry_type, NULL);
 
-	/* if HAL gives us a set of folders on the device containing audio files, 
+	/* if HAL gives us a set of folders on the device containing audio files,
 	 * load only those folders, otherwise add the whole volume.
 	 */
 	if (priv->audio_folders) {
@@ -503,14 +534,14 @@
 	HandlePlaylistEntryData *data;
 	RBShell *shell;
 
-	g_object_get (G_OBJECT (source), 
-		      "shell", &shell, 
+	g_object_get (G_OBJECT (source),
+		      "shell", &shell,
 		      "entry-type", &entry_type,
 		      NULL);
 
 	playlist = RB_STATIC_PLAYLIST_SOURCE (
-			rb_static_playlist_source_new (shell, 
-						      rel_path, 
+			rb_static_playlist_source_new (shell,
+						      rel_path,
 						      FALSE,
 						      entry_type));
 	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
@@ -518,7 +549,7 @@
 	data = g_new0 (HandlePlaylistEntryData, 1);
 	data->source = playlist;
 	data->player_source = source;
-	
+
 	parser = totem_pl_parser_new ();
 
 	g_signal_connect (parser,
@@ -573,7 +604,7 @@
 	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
 	char *mount_path;
 	char *playlist_path = NULL;
-	
+
 	mount_path = rb_generic_player_source_get_mount_path (source);
 	if (priv->playlist_path) {
 		/* If the device only supports a single playlist, just load that */
@@ -626,6 +657,155 @@
 	return full_uri;
 }
 
+static gboolean
+impl_can_paste (RBSource *source)
+{
+	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
+
+	return (priv->read_only == FALSE);
+}
+
+/* probably should move this somewhere common */
+static char *
+sanitize_path (const char *str)
+{
+	gchar *res = NULL;
+	gchar *s;
+
+	/* Skip leading periods, otherwise files disappear... */
+	while (*str == '.')
+		str++;
+
+	s = g_strdup(str);
+	/* Replace path seperators with a hyphen */
+	g_strdelimit (s, "/", '-');
+
+	/* Replace separators with a hyphen */
+	g_strdelimit (s, "\\:|", '-');
+	/* Replace all other weird characters to whitespace */
+	g_strdelimit (s, "*?&!\'\"$()`>{}", ' ');
+	/* Replace all whitespace with underscores */
+	/* TODO: I'd like this to compress whitespace aswell */
+	g_strdelimit (s, "\t ", '_');
+
+	res = g_filename_from_utf8(s, -1, NULL, NULL, NULL);
+	g_free(s);
+	return res ? res : g_strdup(str);
+}
+
+
+static char *
+impl_build_dest_uri (RBRemovableMediaSource *source, RhythmDBEntry *entry)
+{
+	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
+	const char *mime;
+	char *artist, *album, *title;
+	gulong track_number, disc_number;
+	const char *folders;
+	char *extension;
+	char *number;
+	char *file = NULL;
+	char *path;
+
+	rb_debug ("building dest uri for entry at %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
+
+	artist = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+	album = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+	title = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+
+	/* we really do need to fix this so untagged entries actually have NULL rather than
+	 * a translated string.
+	 */
+	if (strcmp (artist, _("Unknown")) == 0 && strcmp (album, _("Unknown")) == 0 &&
+	    g_str_has_suffix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), title)) {
+		/* file isn't tagged, so just use the filename as-is */
+		file = g_strdup (title);
+	}
+
+	mime = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);
+
+	/* hackish mapping of gstreamer media types to mime types; this
+	 * should be easier when we do proper (deep) typefinding.
+	 */
+	if (strcmp (mime, "audio/x-wav") == 0) {
+		/* if it has a bitrate, assume it's mp3-in-wav */
+		if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE) != 0)
+			mime = "audio/mpeg";
+	} else if (strcmp (mime, "application/x-id3") == 0) {
+		mime = "audio/mpeg";
+	}
+
+	if (priv->output_mime_types == NULL || rb_str_in_strv (mime, priv->output_mime_types)) {
+		char *uri;
+		const char *ext;
+
+		uri = rhythmdb_entry_get_playback_uri (entry);
+		ext = g_utf8_strrchr (uri, -1, '.');
+		if (ext == NULL) {
+			/* uhh.. */
+			extension = g_strdup ("");
+		} else {
+			extension = g_strdup (ext + 1);
+		}
+		g_free (uri);
+	} else {
+		/* needs transcoding */
+		g_warning ("mime type %s not in output mime types list", mime);
+		extension = g_strdup_printf ("FIXME"); /*rb_mime_get_extension (mime);*/
+	}
+
+	if (file == NULL) {
+		track_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER);
+		disc_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER);
+		if (disc_number > 0)
+			number = g_strdup_printf ("%u.%u", (guint)disc_number, (guint)track_number);
+		else
+			number = g_strdup_printf ("%u", (guint)track_number);
+
+		switch (priv->folder_depth) {
+		case 0:
+			/* artist - album - number - title */
+			file = g_strdup_printf ("%s - %s - %s - %s.%s",
+						artist, album, number, title, extension);
+			break;
+
+		case 1:
+			/* artist - album/number - title */
+			file = g_strdup_printf ("%s - %s" G_DIR_SEPARATOR_S "%s - %s.%s",
+						artist, album, number, title, extension);
+			break;
+
+		default: /* use this for players that don't care */
+		case 2:
+			/* artist/album/number - title */
+			file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s - %s.%s",
+						artist, album, number, title, extension);
+			break;
+		}
+		g_free (number);
+	}
+
+	g_free (extension);
+	g_free (artist);
+	g_free (album);
+	g_free (title);
+
+	if (file == NULL)
+		return NULL;
+
+	if (priv->audio_folders && priv->audio_folders[0])
+		folders = priv->audio_folders[0];
+	else
+		folders = "";
+
+	path = g_build_filename (priv->mount_path, folders, file, NULL);
+	g_free (file);
+
+	/* TODO: check for duplicates, or just overwrite by default? */
+	rb_debug ("dest file is %s", path);
+	return path;
+}
+
 /* generic HAL-related code */
 
 #ifdef HAVE_HAL_0_5
@@ -720,3 +900,4 @@
 
 
 #endif
+
diff -rN -u old-pie2/plugins/generic-player/rb-generic-player-source.h new-pie2/plugins/generic-player/rb-generic-player-source.h
--- old-pie2/plugins/generic-player/rb-generic-player-source.h	2006-08-13 16:40:32.000000000 +1000
+++ new-pie2/plugins/generic-player/rb-generic-player-source.h	2006-08-13 16:40:32.000000000 +1000
@@ -46,9 +46,15 @@
 
 	char *		(*impl_get_mount_path) (RBGenericPlayerSource *source);
 	void		(*impl_load_playlists) (RBGenericPlayerSource *source);
+	char **		(*impl_get_audio_folders) (RBGenericPlayerSource *source);
 
 	/* used internally in the base load_playlist implementation*/
 	char *		(*impl_transform_playlist_uri) (RBGenericPlayerSource *source, const char *uri);
+
+#if ENABLE_TRACK_TRANSFER
+	/* used for track transfer - returns the filename relative to the audio folder on the device */
+	char *		(*impl_build_filename) (RBGenericPlayerSource *source, RhythmDBEntry *entry);
+#endif
 } RBGenericPlayerSourceClass;
 
 RBRemovableMediaSource *	rb_generic_player_source_new		(RBShell *shell, GnomeVFSVolume *volume);

diff -rN -u old-pie2/sources/rb-ipod-source.c new-pie2/sources/rb-ipod-source.c
--- old-pie2/sources/rb-ipod-source.c	2006-08-13 16:40:32.000000000 +1000
+++ new-pie2/sources/rb-ipod-source.c	2006-08-13 16:40:32.000000000 +1000
@@ -69,12 +69,15 @@
 #endif
 
 #ifdef ENABLE_IPOD_WRITING
-static void impl_paste (RBSource *source, GList *entries);
-static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data);
-static gchar *
-ipod_get_filename_for_uri (const gchar *mount_point, const gchar *uri_str);
-static gchar *
-ipod_path_from_unix_path (const gchar *mount_point, const gchar *unix_path);
+static gboolean impl_track_added (RBRemovableMediaSource *source,
+				  RhythmDBEntry *entry,
+				  const char *dest);
+static char* impl_build_dest_uri (RBRemovableMediaSource *source,
+					RhythmDBEntry *entry);
+static gchar* ipod_get_filename_for_uri (const gchar *mount_point,
+					 const gchar *uri_str);
+static gchar* ipod_path_from_unix_path (const gchar *mount_point,
+					const gchar *unix_path);
 #endif
 static void itdb_schedule_save (Itdb_iTunesDB *db);
 
@@ -96,6 +99,7 @@
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+	RBRemovableMediaSourceClass *rms_class = RB_REMOVABLE_MEDIA_SOURCE_CLASS (klass);
 
 	object_class->constructor = rb_ipod_source_constructor;
 	object_class->dispose = rb_ipod_source_dispose;
@@ -106,10 +110,14 @@
    	source_class->impl_move_to_trash = impl_move_to_trash;
 	source_class->impl_can_rename = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_get_ui_actions = impl_get_ui_actions;
+
 #ifdef ENABLE_IPOD_WRITING
-  	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
-  	source_class->impl_paste = impl_paste;
-  	source_class->impl_receive_drag = impl_receive_drag;
+	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
+	rms_class->impl_track_added = impl_track_added;
+	rms_class->impl_build_dest_uri = impl_build_dest_uri;
+#else
+	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_false_function;
+	rms_class->impl_track_added = NULL;
 #endif
 
 	g_type_class_add_private (klass, sizeof (RBiPodSourcePrivate));
@@ -875,17 +883,17 @@
 
 #ifdef ENABLE_IPOD_WRITING
 static char *
-build_filename (RBSource *asource, RhythmDBEntry *entry)
+impl_build_dest_uri (RBRemovableMediaSource *source, RhythmDBEntry *entry)
 {
+	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (source);
 	const char *uri;
 	char *dest;
-	char *dest_uri;
-
-	RBiPodSourcePrivate *priv = IPOD_SOURCE_GET_PRIVATE (asource);
 
 	uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
 	dest = ipod_get_filename_for_uri (priv->ipod_mount_path,  uri);
 	if (dest != NULL) {
+		char *dest_uri;
+
 		dest_uri = g_filename_to_uri (dest, NULL, NULL);
 		g_free (dest);
 		return dest_uri;
@@ -894,13 +902,14 @@
 	return NULL;
 }
 
-static void
-completed_cb (RhythmDBEntry *entry, const char *dest, RBiPodSource *source)
+static gboolean
+impl_track_added (RBRemovableMediaSource *source, RhythmDBEntry *entry, const char *dest)
 {
+	RBiPodSource *isource = RB_IPOD_SOURCE (source);
 	RhythmDB *db;
 	Itdb_Track *song;
 
-        db = get_db_for_source (source);
+        db = get_db_for_source (isource);
 
 	song = create_ipod_song_from_entry (entry);
 	if (song != NULL) {
@@ -914,103 +923,13 @@
 		itdb_playlist_add_track (itdb_playlist_mpl (priv->ipod_db),
 					 song, -1);
 
-		add_ipod_song_to_db (source, db, song);
+		add_ipod_song_to_db (isource, db, song);
 		itdb_schedule_save (priv->ipod_db);
 	}
 
 	g_object_unref (db);
-}
-
-static void
-impl_paste (RBSource *asource, GList *entries)
-{
-	RBRemovableMediaManager *rm_mgr;
-	GList *l;
-	RBShell *shell;
-
-	g_object_get (asource, "shell", &shell, NULL);
-	g_object_get (shell,
-		      "removable-media-manager", &rm_mgr,
-		      NULL);
-	g_object_unref (shell);
-
-	for (l = entries; l != NULL; l = l->next) {
-		RhythmDBEntry *entry;
-		RhythmDBEntryType entry_type;
-		RhythmDBEntryType ipod_entry_type;
-		char *dest;
-
-		entry = (RhythmDBEntry *)l->data;
-		entry_type = rhythmdb_entry_get_entry_type (entry);
-		g_object_get (asource,
-			      "entry-type", &ipod_entry_type,
-			      NULL);
-		if (entry_type == RHYTHMDB_ENTRY_TYPE_IRADIO_STATION ||
-		    entry_type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED ||
-		    /*entry_type == RHYTHMDB_ENTRY_TYPE_PODCAST_EPISODE ||*/
-		    entry_type == ipod_entry_type) {
-			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
-			continue;
-		}
 
-		dest = build_filename (asource, entry);
-		if (dest == NULL) {
-			rb_debug ("could not create destination path for entry");
-			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
-			continue;
-		}
-		rb_removable_media_manager_queue_transfer (rm_mgr, entry,
-							   dest, NULL,
-							   (RBTranferCompleteCallback)completed_cb, asource);
-		g_free (dest);
-		g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
-	}
-
-	g_object_unref (rm_mgr);
-}
-
-static gboolean
-impl_receive_drag (RBSource *asource, GtkSelectionData *data)
-{
-	RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
-	GList *list, *i;
-	GList *entries = NULL;
-	RhythmDB *db;
-
-	rb_debug ("parsing uri list");
-	list = rb_uri_list_parse ((const char *) data->data);
-
-        db = get_db_for_source (RB_IPOD_SOURCE (source));
-
-	for (i = list; i != NULL; i = g_list_next (i)) {
-		if (i->data != NULL) {
-			char *uri = i->data;
-			RhythmDBEntry *entry;
-
-			entry = rhythmdb_entry_lookup_by_location (db, uri);
-
-			if (entry == NULL) {
-				/* add to the library */
-				g_print ("Where does that come from?\n");
-			} else {
-				/* add to list of entries to copy */
-				entries = g_list_prepend (entries, entry);
-			}
-
-			g_free (uri);
-		}
-	}
-	g_object_unref (db);
-	g_list_free (list);
-
-	if (entries) {
-		entries = g_list_reverse (entries);
-		if (rb_source_can_paste (asource))
-			rb_source_paste (asource, entries);
-		g_list_free (entries);
-	}
-
-	return TRUE;
+	return FALSE;
 }
 
 /* Generation of the filename for the ipod */
diff -rN -u old-pie2/sources/rb-removable-media-source.c new-pie2/sources/rb-removable-media-source.c
--- old-pie2/sources/rb-removable-media-source.c	2006-08-13 16:40:32.000000000 +1000
+++ new-pie2/sources/rb-removable-media-source.c	2006-08-13 16:40:32.000000000 +1000
@@ -35,6 +35,7 @@
 #include "rhythmdb.h"
 #include "eel-gconf-extensions.h"
 #include "rb-removable-media-source.h"
+#include "rb-removable-media-manager.h"
 #include "rb-stock-icons.h"
 #include "rb-debug.h"
 #include "rb-dialog.h"
@@ -55,6 +56,8 @@
 			                  GParamSpec *pspec);
 
 static void impl_delete_thyself (RBSource *source);
+static void impl_paste (RBSource *source, GList *entries);
+static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data);
 
 typedef struct
 {
@@ -87,9 +90,9 @@
 	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
-	source_class->impl_receive_drag = NULL;
+  	source_class->impl_paste = impl_paste;
+  	source_class->impl_receive_drag = impl_receive_drag;
 	source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
-	source_class->impl_paste = NULL;
 	source_class->impl_delete = NULL;
 	source_class->impl_get_config_widget = NULL;
 	source_class->impl_show_popup = (RBSourceFeatureFunc) rb_false_function;
@@ -228,3 +231,148 @@
 	rhythmdb_commit (db);
 	g_object_unref (db);
 }
+
+
+static void
+_track_added_cb (RhythmDBEntry *entry, const char *uri, RBRemovableMediaSource *source)
+{
+	rb_removable_media_source_track_added (source, entry, uri);
+}
+
+static void
+impl_paste (RBSource *source, GList *entries)
+{
+	RBRemovableMediaManager *rm_mgr;
+	RBShell *shell;
+	GList *l;
+	RhythmDBEntryType our_entry_type;
+
+	g_object_get (source, "shell", &shell, NULL);
+	g_object_get (shell,
+		      "removable-media-manager", &rm_mgr,
+		      NULL);
+	g_object_unref (shell);
+
+	g_object_get (source,
+		      "entry-type", &our_entry_type,
+		      NULL);
+
+	for (l = entries; l != NULL; l = l->next) {
+		RhythmDBEntry *entry;
+		RhythmDBEntryType entry_type;
+		char *dest;
+
+		entry = (RhythmDBEntry *)l->data;
+		entry_type = rhythmdb_entry_get_entry_type (entry);
+		if (entry_type == RHYTHMDB_ENTRY_TYPE_IRADIO_STATION ||
+		    entry_type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED ||
+		    /*entry_type == RHYTHMDB_ENTRY_TYPE_PODCAST_EPISODE ||*/
+		    entry_type == our_entry_type) {
+			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+			continue;
+		}
+
+		dest = rb_removable_media_source_build_dest_uri (RB_REMOVABLE_MEDIA_SOURCE (source), entry);
+		if (dest == NULL) {
+			rb_debug ("could not create destination path for entry");
+			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+			continue;
+		}
+
+		rb_removable_media_manager_queue_transfer (rm_mgr, entry,
+							   dest, NULL,
+							   (RBTranferCompleteCallback)_track_added_cb, source);
+		g_free (dest);
+		g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	}
+
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, our_entry_type);
+	g_object_unref (rm_mgr);
+}
+
+static gboolean
+impl_receive_drag (RBSource *asource, GtkSelectionData *data)
+{
+	RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
+	GList *list, *i;
+	GList *entries = NULL;
+	RBShell *shell;
+	RhythmDB *db;
+
+	if (!rb_source_can_paste (asource))
+		return FALSE;
+
+  	g_object_get (source, "shell", &shell, NULL);
+  	g_object_get (shell, "db", &db, NULL);
+  	g_object_unref (shell);
+
+	rb_debug ("parsing uri list");
+	list = rb_uri_list_parse ((const char *) data->data);
+
+	for (i = list; i != NULL; i = g_list_next (i)) {
+		if (i->data != NULL) {
+			char *uri = i->data;
+			RhythmDBEntry *entry;
+
+			entry = rhythmdb_entry_lookup_by_location (db, uri);
+
+			if (entry == NULL) {
+				/* add to the library */
+				g_print ("Where does that come from?\n");
+			} else {
+				/* add to list of entries to copy */
+				entries = g_list_prepend (entries, entry);
+			}
+
+			g_free (uri);
+		}
+	}
+	g_object_unref (db);
+	g_list_free (list);
+
+	if (entries) {
+		entries = g_list_reverse (entries);
+		rb_source_paste (asource, entries);
+		g_list_free (entries);
+	}
+
+	return TRUE;
+}
+
+char*
+rb_removable_media_source_build_dest_uri (RBRemovableMediaSource *source, RhythmDBEntry *entry)
+{
+	RBRemovableMediaSourceClass *klass = RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source);
+
+	if (klass->impl_build_dest_uri)
+		return klass->impl_build_dest_uri (source, entry);
+	else
+		return NULL;
+}
+
+void
+rb_removable_media_source_track_added (RBRemovableMediaSource *source, RhythmDBEntry *entry, const char *uri)
+{
+	RBRemovableMediaSourceClass *klass = RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source);
+	gboolean add_to_db = TRUE;
+
+	if (klass->impl_track_added)
+		add_to_db = klass->impl_track_added (source, entry, uri);
+
+	if (add_to_db) {
+		RhythmDBEntryType entry_type;
+		RhythmDB *db;
+		RBShell *shell;
+
+		g_object_get (source, "shell", &shell, NULL);
+		g_object_get (shell, "db", &db, NULL);
+		g_object_unref (shell);
+
+		g_object_get (source, "entry-type", &entry_type, NULL);
+		rhythmdb_add_uri_with_type (db, uri, entry_type);
+		g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+
+		g_object_unref (db);
+	}
+}
+
diff -rN -u old-pie2/sources/rb-removable-media-source.h new-pie2/sources/rb-removable-media-source.h
--- old-pie2/sources/rb-removable-media-source.h	2006-08-13 16:40:32.000000000 +1000
+++ new-pie2/sources/rb-removable-media-source.h	2006-08-13 16:40:32.000000000 +1000
@@ -43,10 +43,22 @@
 typedef struct
 {
 	RBBrowserSourceClass parent;
+
+	gboolean	(*impl_track_added)		(RBRemovableMediaSource *source,
+							 RhythmDBEntry *entry,
+							 const char *uri);
+	char *		(*impl_build_dest_uri)		(RBRemovableMediaSource *source,
+							 RhythmDBEntry *entry);
 } RBRemovableMediaSourceClass;
 
 GType			rb_removable_media_source_get_type	(void);
 
+char*		rb_removable_media_source_build_dest_uri 	(RBRemovableMediaSource *source,
+								 RhythmDBEntry *entry);
+void		rb_removable_media_source_track_added		(RBRemovableMediaSource *source,
+								 RhythmDBEntry *entry,
+								 const char *uri);
+
 G_END_DECLS
 
 #endif /* __RB_REMOVABLE_MEDIA_SOURCE_H */

