diff -u -p -r1.3 rb-encoder.c
--- backends/rb-encoder.c	14 Jul 2006 12:22:39 -0000	1.3
+++ backends/rb-encoder.c	11 Dec 2006 06:01:58 -0000
@@ -94,11 +94,11 @@ gboolean
 rb_encoder_encode (RBEncoder *encoder,
 		   RhythmDBEntry *entry,
 		   const char *dest,
-		   const char *mime_type)
+		   GList *mime_types)
 {
 	RBEncoderIface *iface = RB_ENCODER_GET_IFACE (encoder);
 
-	return iface->encode (encoder, entry, dest, mime_type);
+	return iface->encode (encoder, entry, dest, mime_types);
 }
 
 void
@@ -109,6 +109,17 @@ rb_encoder_cancel (RBEncoder *encoder)
 	iface->cancel (encoder);
 }
 
+gboolean
+rb_encoder_get_preferred_mimetype (RBEncoder *encoder,
+				   GList *mime_types,
+				   char **mime,
+				   char **extension)
+{
+	RBEncoderIface *iface = RB_ENCODER_GET_IFACE (encoder);
+
+	return iface->get_preferred_mimetype (encoder, mime_types, mime, extension);
+}
+
 RBEncoder*
 rb_encoder_new (void)
 {
@@ -142,3 +153,4 @@ rb_encoder_error_quark (void)
 
 	return quark;
 }
+
diff -u -p -r1.4 rb-encoder.h
--- backends/rb-encoder.h	24 Sep 2006 12:31:46 -0000	1.4
+++ backends/rb-encoder.h	11 Dec 2006 06:01:58 -0000
@@ -55,8 +55,12 @@ typedef struct
 	gboolean	(*encode)	(RBEncoder *encoder,
 					 RhythmDBEntry *entry,
 					 const char *dest,
-					 const char *mime_type);
+					 GList *mime_types);
 	void		(*cancel)	(RBEncoder *encoder);
+	gboolean	(*get_preferred_mimetype)	(RBEncoder *encoder,
+							 GList *mime_types,
+							 char **mime,
+							 char **extension);
 
 	/* signals */
 	void (*progress) (RBEncoder *encoder,  double fraction);
@@ -70,10 +74,15 @@ GType rb_encoder_get_type (void);
 gboolean	rb_encoder_encode	(RBEncoder *encoder,
 					 RhythmDBEntry *entry,
 					 const char *dest,
-					 const char *mime_type);
+					 GList *mime_types);
 void		rb_encoder_cancel	(RBEncoder *encoder);
 
-/* obly to be used by subclasses */
+gboolean	rb_encoder_get_preferred_mimetype (RBEncoder *encoder,
+						   GList *mime_types,
+						   char **mime,
+						   char **extension);
+
+/* only to be used by subclasses */
 void	_rb_encoder_emit_progress (RBEncoder *encoder, double fraction);
 void	_rb_encoder_emit_completed (RBEncoder *encoder);
 void	_rb_encoder_emit_error (RBEncoder *encoder, GError *error);
diff -u -p -r1.18 rb-encoder-gst.c
--- backends/gstreamer/rb-encoder-gst.c	24 Sep 2006 12:31:47 -0000	1.18
+++ backends/gstreamer/rb-encoder-gst.c	11 Dec 2006 06:01:59 -0000
@@ -67,7 +67,8 @@ struct _RBEncoderGstPrivate {
 	gboolean error_emitted;
 	gboolean completion_emitted;
 
-	gulong total_length;
+	GstFormat position_format;
+	guint64 total_length;
 	guint progress_id;
 };
 
@@ -79,10 +80,15 @@ G_DEFINE_TYPE_WITH_CODE(RBEncoderGst, rb
 static gboolean rb_encoder_gst_encode (RBEncoder *encoder,
 				       RhythmDBEntry *entry,
 				       const char *dest,
-				       const char *mime_type);
+				       GList *mime_types);
 static void rb_encoder_gst_cancel (RBEncoder *encoder);
+static gboolean rb_encoder_gst_get_preferred_mimetype (RBEncoder *encoder,
+						       GList *mime_types,
+						       char **mime,
+						       char **extension);
 static void rb_encoder_gst_emit_completed (RBEncoderGst *encoder);
 
+
 static void
 rb_encoder_gst_class_init (RBEncoderGstClass *klass)
 {
@@ -104,6 +110,7 @@ rb_encoder_init (RBEncoderIface *iface)
 {
 	iface->encode = rb_encoder_gst_encode;
 	iface->cancel = rb_encoder_gst_cancel;
+	iface->get_preferred_mimetype = rb_encoder_gst_get_preferred_mimetype;
 }
 
 static void
@@ -247,20 +254,21 @@ gst_error_cb (GstElement *element,
 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 +277,20 @@ progress_timeout_cb (RBEncoderGst *encod
 	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;
 }
@@ -614,7 +627,7 @@ encoder_match_mime (GstElement *encoder,
 	caps = gst_pad_get_caps (srcpad);
 	structure = gst_caps_get_structure (caps, 0);
 	pad_mime = gst_structure_get_name (structure);
-	match = strcmp (mime_type, pad_mime) == 0;
+	match = (strcmp (mime_type, pad_mime) == 0);
 	gst_caps_unref (caps);
 	gst_object_unref (GST_OBJECT (srcpad));
 
@@ -709,6 +722,22 @@ get_profile_from_mime_type (const char *
 	return matching_profile;
 }
 
+static GMAudioProfile*
+get_profile_from_mime_types (GList *mime_types)
+{
+	GList *l;
+
+	for (l = mime_types; l != NULL; l = g_list_next (l)) {
+		GMAudioProfile *profile;
+
+		profile = get_profile_from_mime_type ((const char *)l->data);
+		if (profile)
+			return profile;
+	}
+
+	return NULL;
+}
+
 static GstElement *
 create_pipeline_and_source (RBEncoderGst *encoder,
 			    RhythmDBEntry *entry,
@@ -830,7 +859,7 @@ static gboolean
 transcode_track (RBEncoderGst *encoder,
 	 	 RhythmDBEntry *entry,
 		 const char *dest,
-		 const char *mime_type,
+		 GList *mime_types,
 		 GError **error)
 {
 	/* src ! decodebin ! queue ! encoding_profile ! queue ! sink */
@@ -839,13 +868,13 @@ transcode_track (RBEncoderGst *encoder,
 
 	g_assert (encoder->priv->pipeline == NULL);
 
-	profile = get_profile_from_mime_type (mime_type);
+	profile = get_profile_from_mime_types (mime_types);
 	if (profile == NULL) {
 		g_set_error (error,
 			     RB_ENCODER_ERROR,
 			     RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
 			     "Unable to locate encoding profile for mime-type "
-			     "'%s'", mime_type);
+			     /*"'%s'", mime_type*/);
 		return FALSE;
 	} else {
 		rb_debug ("selected profile %s",
@@ -931,7 +960,7 @@ static gboolean
 rb_encoder_gst_encode (RBEncoder *encoder,
 		       RhythmDBEntry *entry,
 		       const char *dest,
-		       const char *mime_type)
+		       GList *mime_types)
 {
 	RBEncoderGstPrivate *priv = RB_ENCODER_GST (encoder)->priv;
 	const char *entry_mime_type;
@@ -939,14 +968,13 @@ rb_encoder_gst_encode (RBEncoder *encode
 	gboolean result;
 	GError *error = NULL;
 	GnomeVFSResult vfsresult;
+	gboolean copy;
 
 	g_return_val_if_fail (priv->pipeline == NULL, FALSE);
 
 	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);
-
 	vfsresult = create_parent_dirs (dest);
 	if (vfsresult != GNOME_VFS_OK) {
 		error = g_error_new_literal (RB_ENCODER_ERROR,
@@ -959,14 +987,34 @@ rb_encoder_gst_encode (RBEncoder *encode
 		return FALSE;
 	}
 
-	if ((mime_type == NULL && !was_raw) || (mime_type && (strcmp (mime_type, entry_mime_type) == 0))) {
+	if (mime_types == NULL) {
+		/* don't copy raw audio */
+		copy = !was_raw;
+	} else {
+		GList *l;
+
+		/* see if it's already in any of the destination formats */
+		copy = FALSE;
+		for (l = mime_types; l != NULL; l = g_list_next (l)) {
+			if (strcmp (entry_mime_type, l->data) == 0) {
+				copy = TRUE;
+				break;
+			}
+		}
+	}
+
+	if (copy) {
 		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) {
+		if (mime_types == NULL) {
 			result = extract_track (RB_ENCODER_GST (encoder), entry, dest, &error);
 		} else {
-			result = transcode_track (RB_ENCODER_GST (encoder), entry, dest, mime_type, &error);
+			result = transcode_track (RB_ENCODER_GST (encoder), entry, dest, mime_types, &error);
 		}
+		priv->total_length = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
+		priv->position_format = GST_FORMAT_TIME;
 	}
 
 	if (error) {
@@ -983,4 +1031,32 @@ rb_encoder_gst_encode (RBEncoder *encode
 	}
 
 	return result;
+}
+
+static gboolean
+rb_encoder_gst_get_preferred_mimetype (RBEncoder *encoder,
+				       GList *mime_types,
+				       char **mime,
+				       char **extension)
+{
+	GList *l;
+
+	g_return_val_if_fail (mime_types != NULL, FALSE);
+	g_return_val_if_fail (mime != NULL, FALSE);
+	g_return_val_if_fail (extension != NULL, FALSE);
+
+	for (l = mime_types; l != NULL; l = g_list_next (l)) {
+		GMAudioProfile *profile;
+		const char *mimetype;
+
+		mimetype = (const char *)l->data;
+		profile = get_profile_from_mime_type (mimetype);
+		if (profile) {
+			*extension = g_strdup (gm_audio_profile_get_extension (profile));
+			*mime = g_strdup (mimetype);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
 }
diff -u -p -r1.33 rb.defs
--- bindings/python/rb.defs	30 Oct 2006 13:51:09 -0000	1.33
+++ bindings/python/rb.defs	11 Dec 2006 06:01:59 -0000
@@ -1587,6 +1587,56 @@
   (return-type "GType")
 )
 
+(define-method build_dest_uri
+  (of-object "RBRemovableMediaSource")
+  (c-name "rb_removable_media_source_build_dest_uri")
+  (return-type "char*")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+    '("const-char*" "extension")
+  )
+)
+
+(define-method track_added
+  (of-object "RBRemovableMediaSource")
+  (c-name "rb_removable_media_source_track_added")
+  (return-type "none")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+    '("const-char*" "uri")
+  )
+)
+
+(define-method get_mime_types
+  (of-object "RBRemovableMediaSource")
+  (c-name "rb_removable_media_source_get_mime_types")
+  (return-type "GList*")
+)
+
+(define-virtual impl_build_dest_uri
+  (of-object "RBRemovableMediaSource")
+  (return-type "char*")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+    '("const-char*" "extension")
+  )
+)
+
+(define-virtual impl_track_added
+  (of-object "RBRemovableMediaSource")
+  (return-type "none")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+    '("const-char*" "uri")
+  )
+)
+
+(define-virtual impl_get_mime_types
+  (of-object "RBRemovableMediaSource")
+  (return-type "GList*")
+)
+
+
 
 ;; From ../../shell/rb-removable-media-manager.h
 
@@ -1608,7 +1658,7 @@
   (parameters
     '("RhythmDBEntry*" "entry")
     '("const-char*" "dest")
-    '("const-char*" "mime_type")
+    '("GList*" "mime_type")
     '("RBTranferCompleteCallback" "callback")
     '("gpointer" "userdata")
   )
diff -u -p -r1.32 rb-util.c
--- lib/rb-util.c	2 Oct 2006 14:04:19 -0000	1.32
+++ lib/rb-util.c	11 Dec 2006 06:02:00 -0000
@@ -858,3 +858,19 @@ rb_value_free (GValue *val)
 	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 -u -p -r1.24 rb-util.h
--- lib/rb-util.h	30 Jul 2006 07:37:52 -0000	1.24
+++ lib/rb-util.h	11 Dec 2006 06:02:00 -0000
@@ -58,6 +58,8 @@ gboolean rb_string_list_equal (GList *a,
 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 -u -p -r1.20 rb-generic-player-source.c
--- plugins/generic-player/rb-generic-player-source.c	6 Nov 2006 22:04:25 -0000	1.20
+++ plugins/generic-player/rb-generic-player-source.c	11 Dec 2006 06:02:00 -0000
@@ -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,11 @@ static void rb_generic_player_source_get
 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 GList* impl_get_mime_types (RBRemovableMediaSource *source);
+static char* impl_build_dest_uri (RBRemovableMediaSource *source,
+				  RhythmDBEntry *entry,
+				  const char *extension);
 
 static gchar *default_get_mount_path (RBGenericPlayerSource *source);
 static void default_load_playlists (RBGenericPlayerSource *source);
@@ -87,6 +93,7 @@ typedef struct
 
 	/* information derived from HAL */
 	char **audio_folders;
+	char **output_mime_types;
 	gboolean playlist_format_unknown;
 	gboolean playlist_format_pls;
 	gboolean playlist_format_m3u;
@@ -104,6 +111,7 @@ rb_generic_player_source_class_init (RBG
 {
 	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;
@@ -112,6 +120,10 @@ rb_generic_player_source_class_init (RBG
 	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;
+	rms_class->impl_get_mime_types = impl_get_mime_types;
 
 	klass->impl_get_mount_path = default_get_mount_path;
 	klass->impl_load_playlists = default_load_playlists;
@@ -185,34 +197,55 @@ rb_generic_player_source_get_device_info
 			/* 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]);
+						}
 					}
 				}
 
@@ -282,6 +315,7 @@ rb_generic_player_source_finalize (GObje
 
 	g_free (priv->mount_path);
 	g_strfreev (priv->audio_folders);
+	g_strfreev (priv->output_mime_types);
 	g_free (priv->playlist_path);
 }
 
@@ -636,6 +670,146 @@ default_transform_playlist_uri (RBGeneri
 	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 GList *
+impl_get_mime_types (RBRemovableMediaSource *source)
+{
+	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
+	GList *list = NULL;
+	char **mime;
+
+	for (mime = priv->output_mime_types; *mime != NULL; mime++) {
+		list = g_list_prepend (list, g_strdup (*mime));
+	}
+	return g_list_reverse (list);
+}
+
+static char *
+impl_build_dest_uri (RBRemovableMediaSource *source, RhythmDBEntry *entry, const char *extension)
+{
+	RBGenericPlayerSourcePrivate *priv = GENERIC_PLAYER_SOURCE_GET_PRIVATE (source);
+	const char *mime;
+	char *artist, *album, *title;
+	gulong track_number, disc_number;
+	const char *folders;
+	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 (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 (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
@@ -730,3 +904,4 @@ free_dbus_error (const char *what, DBusE
 
 
 #endif
+
diff -u -p -r1.4 rb-generic-player-source.h
--- plugins/generic-player/rb-generic-player-source.h	25 May 2006 07:20:48 -0000	1.4
+++ plugins/generic-player/rb-generic-player-source.h	11 Dec 2006 06:02:00 -0000
@@ -46,9 +46,15 @@ typedef struct
 
 	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 -u -p -r1.32 rb-removable-media-manager.c
--- shell/rb-removable-media-manager.c	2 Sep 2006 13:41:16 -0000	1.32
+++ shell/rb-removable-media-manager.c	11 Dec 2006 06:02:00 -0000
@@ -639,7 +639,7 @@ typedef struct {
 	RBRemovableMediaManager *manager;
 	RhythmDBEntry *entry;
 	char *dest;
-	char *mime_type;
+	GList *mime_types;
 	gboolean failed;
 	RBTranferCompleteCallback callback;
 	gpointer userdata;
@@ -692,7 +692,7 @@ completed_cb (RBEncoder *encoder, Transf
 
 	g_object_unref (G_OBJECT (encoder));
 	g_free (data->dest);
-	g_free (data->mime_type);
+	rb_list_deep_free (data->mime_types);
 	g_free (data);
 }
 
@@ -731,14 +731,14 @@ do_transfer (RBRemovableMediaManager *ma
 	g_signal_connect (G_OBJECT (encoder),
 			  "completed", G_CALLBACK (completed_cb),
 			  data);
-	rb_encoder_encode (encoder, data->entry, data->dest, NULL);
+	rb_encoder_encode (encoder, data->entry, data->dest, data->mime_types);
 }
 
 void
 rb_removable_media_manager_queue_transfer (RBRemovableMediaManager *manager,
 					  RhythmDBEntry *entry,
 					  const char *dest,
-					  const char *mime_type,
+					  GList *mime_types,
 					  RBTranferCompleteCallback callback,
 					  gpointer userdata)
 {
@@ -751,7 +751,7 @@ rb_removable_media_manager_queue_transfe
 	data->manager = manager;
 	data->entry = entry;
 	data->dest = g_strdup (dest);
-	data->mime_type = g_strdup (mime_type);
+	data->mime_types = rb_string_list_copy (mime_types);
 	data->callback = callback;
 	data->userdata = userdata;
 
diff -u -p -r1.6 rb-removable-media-manager.h
--- shell/rb-removable-media-manager.h	14 Jul 2006 12:30:51 -0000	1.6
+++ shell/rb-removable-media-manager.h	11 Dec 2006 06:02:00 -0000
@@ -71,7 +71,7 @@ void			rb_removable_media_manager_scan (
 void	rb_removable_media_manager_queue_transfer (RBRemovableMediaManager *mgr,
 						   RhythmDBEntry *entry,
 						   const char *dest,
-						   const char *mime_type,
+						   GList *mime_types,
 						   RBTranferCompleteCallback callback,
 						   gpointer userdata);
 #endif
diff -u -p -r1.71 rb-ipod-source.c
--- sources/rb-ipod-source.c	8 Dec 2006 23:44:21 -0000	1.71
+++ sources/rb-ipod-source.c	11 Dec 2006 06:02:00 -0000
@@ -70,12 +70,17 @@ static gboolean hal_udi_is_ipod (const c
 #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 GList * impl_get_mime_types (RBRemovableMediaSource *source);
+static gboolean impl_track_added (RBRemovableMediaSource *source,
+				  RhythmDBEntry *entry,
+				  const char *dest);
+static char* impl_build_dest_uri (RBRemovableMediaSource *source,
+				  RhythmDBEntry *entry,
+				  const char *extension);
+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);
 
@@ -101,6 +106,7 @@ rb_ipod_source_class_init (RBiPodSourceC
 {
 	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;
@@ -111,10 +117,15 @@ rb_ipod_source_class_init (RBiPodSourceC
    	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;
+	rms_class->impl_get_mime_types = impl_get_mime_types;
+#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));
@@ -895,17 +906,17 @@ itdb_schedule_save (Itdb_iTunesDB *db)
 
 #ifdef ENABLE_IPOD_WRITING
 static char *
-build_filename (RBSource *asource, RhythmDBEntry *entry)
+impl_build_dest_uri (RBRemovableMediaSource *source, RhythmDBEntry *entry, const char *extension)
 {
+	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;
@@ -914,13 +925,14 @@ build_filename (RBSource *asource, Rhyth
 	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) {
@@ -934,102 +946,14 @@ completed_cb (RhythmDBEntry *entry, cons
 		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);
+	return FALSE;
 }
 
-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 == ipod_entry_type ||
-		    entry_type->category != RHYTHMDB_ENTRY_NORMAL) {
-			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;
-}
 
 /* Generation of the filename for the ipod */
 
@@ -1264,4 +1188,16 @@ impl_delete_thyself (RBSource *source)
 	priv->ipod_db = NULL;
 
 	RB_SOURCE_CLASS (rb_ipod_source_parent_class)->impl_delete_thyself (source);
+}
+
+static GList *
+impl_get_mime_types (RBRemovableMediaSource *source)
+{
+	GList *ret = NULL;
+
+	/* FIXME: we should really query HAL for this */
+	ret = g_list_prepend (ret, g_strdup ("audio/aac"));
+	ret = g_list_prepend (ret, g_strdup ("audio/mpeg"));
+
+	return ret;
 }
diff -u -p -r1.18 rb-removable-media-source.c
--- sources/rb-removable-media-source.c	17 Jul 2006 14:52:43 -0000	1.18
+++ sources/rb-removable-media-source.c	11 Dec 2006 06:02:00 -0000
@@ -35,6 +35,8 @@
 #include "rhythmdb.h"
 #include "eel-gconf-extensions.h"
 #include "rb-removable-media-source.h"
+#include "rb-removable-media-manager.h"
+#include "rb-encoder.h"
 #include "rb-stock-icons.h"
 #include "rb-debug.h"
 #include "rb-dialog.h"
@@ -55,6 +57,8 @@ static void rb_removable_media_source_ge
 			                  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 +91,9 @@ rb_removable_media_source_class_init (RB
 	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 +232,218 @@ impl_delete_thyself (RBSource *source)
 	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;
+	RBEncoder *encoder;
+
+	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);
+
+	encoder = rb_encoder_new ();
+
+	for (l = entries; l != NULL; l = l->next) {
+		RhythmDBEntry *entry;
+		RhythmDBEntryType entry_type;
+		GList *mime_types;
+		char *mimetype;
+		char *extension;
+		char *dest;
+
+		dest = NULL;
+		mimetype = NULL;
+		extension = NULL;
+		mime_types = NULL;
+		entry = (RhythmDBEntry *)l->data;
+		entry_type = rhythmdb_entry_get_entry_type (entry);
+
+		if (entry_type == our_entry_type ||
+		    entry_type->category != RHYTHMDB_ENTRY_NORMAL) {
+			goto impl_paste_end;
+		}
+
+		mime_types = rb_removable_media_source_get_mime_types (RB_REMOVABLE_MEDIA_SOURCE (source));
+		if (!rb_encoder_get_preferred_mimetype (encoder, mime_types, &mimetype, &extension)) {
+			rb_debug ("failed to find acceptable mime type for %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
+			goto impl_paste_end;
+
+		}
+
+		dest = rb_removable_media_source_build_dest_uri (RB_REMOVABLE_MEDIA_SOURCE (source), entry, extension);
+		if (dest == NULL) {
+			rb_debug ("could not create destination path for entry");
+			goto impl_paste_end;
+		}
+
+		rb_list_deep_free (mime_types);
+		mime_types = g_list_prepend (NULL, g_strdup (mimetype));
+		rb_removable_media_manager_queue_transfer (rm_mgr, entry,
+							   dest, mime_types,
+							   (RBTranferCompleteCallback)_track_added_cb, source);
+impl_paste_end:
+		g_free (dest);
+		g_free (mimetype);
+		g_free (extension);
+		if (mime_types)
+			rb_list_deep_free (mime_types);
+		if (entry_type)
+			g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	}
+
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, our_entry_type);
+	g_object_unref (rm_mgr);
+	g_object_unref (encoder);
+}
+
+static RhythmDB *
+get_db_for_source (RBSource *source)
+{
+	RBShell *shell;
+	RhythmDB *db;
+
+  	g_object_get (source, "shell", &shell, NULL);
+  	g_object_get (shell, "db", &db, NULL);
+  	g_object_unref (shell);
+
+        return db;
+}
+
+static gboolean
+impl_receive_drag (RBSource *asource, GtkSelectionData *data)
+{
+	GList *entries;
+	RhythmDB *db;
+	char *type;
+
+	entries = NULL;
+	type = gdk_atom_name (data->type);
+        db = get_db_for_source (asource);
+
+	if (strcmp (type, "text/uri-list") == 0) {
+		GList *list;
+		GList *i;
+	
+		rb_debug ("parsing uri list");
+		list = rb_uri_list_parse ((const char *) data->data);
+
+		for (i = list; i != NULL; i = g_list_next (i)) {
+			char *uri;
+			RhythmDBEntry *entry;
+
+			if (i->data == NULL)
+				continue;
+
+			uri = i->data;
+			entry = rhythmdb_entry_lookup_by_location (db, uri);
+
+			if (entry == NULL) {
+				/* add to the library */
+				rb_debug ("received drop of unknown uri: %s", uri);
+			} else {
+				/* add to list of entries to copy */
+				entries = g_list_prepend (entries, entry);
+			}
+			g_free (uri);
+		}
+		g_list_free (list);
+	} else if (strcmp (type, "application/x-rhythmbox-entry") == 0) {
+		char **list;
+		char **i;
+
+		rb_debug ("parsing entry ids");
+		list = g_strsplit ((const char*)data->data, "\n", -1);
+		for (i = list; *i != NULL; i++) {
+			RhythmDBEntry *entry;
+			gulong id;
+
+			id = atoi (*i);
+			entry = rhythmdb_entry_lookup_by_id (db, id);
+			if (entry != NULL)
+				entries = g_list_prepend (entries, entry);
+		}
+
+		g_strfreev (list);
+	} else {
+		rb_debug ("received unknown drop type");
+	}
+
+	g_object_unref (db);
+	g_free (type);
+
+	if (entries) {
+		entries = g_list_reverse (entries);
+		if (rb_source_can_paste (asource))
+			rb_source_paste (asource, entries);
+		g_list_free (entries);
+	}
+
+	return TRUE;
+}
+
+char*
+rb_removable_media_source_build_dest_uri (RBRemovableMediaSource *source, RhythmDBEntry *entry, const char *extension)
+{
+	RBRemovableMediaSourceClass *klass = RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source);
+
+	if (klass->impl_build_dest_uri)
+		return klass->impl_build_dest_uri (source, entry, extension);
+	else
+		return NULL;
+}
+
+GList *
+rb_removable_media_source_get_mime_types (RBRemovableMediaSource *source)
+{
+	RBRemovableMediaSourceClass *klass = RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source);
+
+	if (klass->impl_get_mime_types)
+		return klass->impl_get_mime_types (source);
+	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 -u -p -r1.4 rb-removable-media-source.h
--- sources/rb-removable-media-source.h	14 Jul 2006 12:31:34 -0000	1.4
+++ sources/rb-removable-media-source.h	11 Dec 2006 06:02:00 -0000
@@ -43,9 +43,25 @@ typedef struct
 typedef struct
 {
 	RBBrowserSourceClass parent;
+
+	gboolean	(*impl_track_added)		(RBRemovableMediaSource *source,
+							 RhythmDBEntry *entry,
+							 const char *uri);
+	char *		(*impl_build_dest_uri)		(RBRemovableMediaSource *source,
+							 RhythmDBEntry *entry,
+							 const char *extension);
+	GList *		(*impl_get_mime_types)		(RBRemovableMediaSource *source);
 } RBRemovableMediaSourceClass;
 
 GType			rb_removable_media_source_get_type	(void);
+
+char*		rb_removable_media_source_build_dest_uri 	(RBRemovableMediaSource *source,
+								 RhythmDBEntry *entry,
+								 const char *extension);
+void		rb_removable_media_source_track_added		(RBRemovableMediaSource *source,
+								 RhythmDBEntry *entry,
+								 const char *uri);
+GList *		rb_removable_media_source_get_mime_types	(RBRemovableMediaSource *source);
 
 G_END_DECLS
 
