diff --git a/ChangeLog b/ChangeLog
index 08dc285..2d02c33 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2008-10-19 Christophe Fergeau  <teuf@gnome.org>
+
+	Patch by: Ed Catmur, Paul Drain and Christophe Fergeau
+	Part of bug 411634 – Copying tracks to iPod can create duplicates
+
+	* plugins/ipod/rb-ipod-source.c: implement should_paste and don't
+	copy files to the ipod if it already contains a song with the same
+	album/artist/title
+
+2008-10-15 Christophe Fergeau  <teuf@gnome.org>
+
+	Patch by: Ed Catmur and Paul Drain
+	Part of bug 411634 – Copying tracks to iPod can create duplicates
+
+	* sources/rb-removable-media-source.c
+	* sources/rb-removable-media-source.h: add should_paste virtual
+	method so that derived sources can indicate whether a paste
+	operation should be completed or not
+
 2008-10-15 Christophe Fergeau  <teuf@gnome.org>
 
 	Patch by: John Daiker
diff --git a/plugins/ipod/rb-ipod-source.c b/plugins/ipod/rb-ipod-source.c
index 3a52927..306044b 100644
--- a/plugins/ipod/rb-ipod-source.c
+++ b/plugins/ipod/rb-ipod-source.c
@@ -77,6 +77,8 @@ static gboolean hal_udi_is_ipod (const char *udi);
 
 #ifdef ENABLE_IPOD_WRITING
 static GList * impl_get_mime_types (RBRemovableMediaSource *source);
+static gboolean impl_should_paste (RBRemovableMediaSource *source,
+				   RhythmDBEntry *entry);
 static gboolean impl_track_added (RBRemovableMediaSource *source,
 				  RhythmDBEntry *entry,
 				  const char *dest,
@@ -158,6 +160,7 @@ rb_ipod_source_class_init (RBiPodSourceClass *klass)
 
 #ifdef ENABLE_IPOD_WRITING
 	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
+	rms_class->impl_should_paste = impl_should_paste;
 	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;
@@ -1363,6 +1366,55 @@ add_to_podcasts (RBiPodSource *source, Itdb_Track *song)
 }
 
 static gboolean
+impl_should_paste (RBRemovableMediaSource *source,
+		   RhythmDBEntry *entry)
+{
+	RhythmDBEntryType entry_type;
+	RhythmDB *db;
+	RBShell *shell;
+	const char *title;
+	const char *album;
+	const char *artist;
+	GtkTreeModel *query_model;
+	GtkTreeIter iter;
+	gboolean no_match;
+	 
+	RBRemovableMediaSourceClass *rms_class = RB_REMOVABLE_MEDIA_SOURCE_CLASS (g_type_class_peek_parent (RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source)));
+	/* chain up to parent impl */
+	if (!rms_class->impl_should_paste (source, entry))
+		return FALSE;
+
+	g_object_get (source, "shell", &shell, "entry-type", &entry_type, NULL);
+	g_object_get (shell, "db", &db, NULL);
+	g_object_unref (shell);
+
+	query_model = GTK_TREE_MODEL (rhythmdb_query_model_new_empty (db));
+	title = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
+	album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
+	artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
+	rhythmdb_do_full_query (db, RHYTHMDB_QUERY_RESULTS (query_model),
+				RHYTHMDB_QUERY_PROP_EQUALS,
+				RHYTHMDB_PROP_TYPE, entry_type,
+				RHYTHMDB_QUERY_PROP_EQUALS,
+				RHYTHMDB_PROP_ARTIST, artist,
+				RHYTHMDB_QUERY_PROP_EQUALS,
+				RHYTHMDB_PROP_ALBUM, album,
+				RHYTHMDB_QUERY_PROP_EQUALS,
+				RHYTHMDB_PROP_TITLE, title,
+				RHYTHMDB_QUERY_END);
+
+	no_match = (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (query_model),
+						 &iter));
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	g_object_unref(query_model);
+	g_object_unref (db);
+	if (no_match == FALSE) {
+		rb_debug ("not adding %s - %s - %s to the ipod since it's already present\n", title, album, artist);
+	}
+	return no_match;
+}
+
+static gboolean
 impl_track_added (RBRemovableMediaSource *source,
 		  RhythmDBEntry *entry,
 		  const char *dest,
diff --git a/sources/rb-removable-media-source.c b/sources/rb-removable-media-source.c
index 3192d13..bd867f6 100644
--- a/sources/rb-removable-media-source.c
+++ b/sources/rb-removable-media-source.c
@@ -68,6 +68,8 @@ static void impl_delete_thyself (RBSource *source);
 static void impl_paste (RBSource *source, GList *entries);
 #endif
 static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data);
+static gboolean impl_should_paste (RBRemovableMediaSource *source,
+				   RhythmDBEntry *entry);
 static guint impl_want_uri (RBSource *source, const char *uri);
 static gboolean impl_uri_is_source (RBSource *source, const char *uri);
 
@@ -118,6 +120,8 @@ rb_removable_media_source_class_init (RBRemovableMediaSourceClass *klass)
 	browser_source_class->impl_get_paned_key = NULL;
 	browser_source_class->impl_has_drop_support = (RBBrowserSourceFeatureFunc) rb_false_function;
 
+	klass->impl_should_paste = impl_should_paste;
+
 	g_object_class_install_property (object_class,
 					 PROP_VOLUME,
 					 g_param_spec_object ("volume",
@@ -363,7 +367,7 @@ impl_paste (RBSource *source, GList *entries)
 		entry_type = rhythmdb_entry_get_entry_type (entry);
 
 		if (entry_type == our_entry_type ||
-		    entry_type->category != RHYTHMDB_ENTRY_NORMAL) {
+		    !rb_removable_media_source_should_paste (RB_REMOVABLE_MEDIA_SOURCE (source), entry)) {
 			goto impl_paste_end;
 		}
 
@@ -634,6 +638,24 @@ rb_removable_media_source_get_mime_types (RBRemovableMediaSource *source)
 		return NULL;
 }
 
+static gboolean
+impl_should_paste (RBRemovableMediaSource *source, RhythmDBEntry *entry)
+{
+	RhythmDBEntryType entry_type = rhythmdb_entry_get_entry_type (entry);
+	gboolean should_paste = (entry_type->category == RHYTHMDB_ENTRY_NORMAL);
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+	return should_paste;
+}
+
+gboolean
+rb_removable_media_source_should_paste (RBRemovableMediaSource *source,
+					RhythmDBEntry *entry)
+{
+	RBRemovableMediaSourceClass *klass = RB_REMOVABLE_MEDIA_SOURCE_GET_CLASS (source);
+
+	return klass->impl_should_paste (source, entry);
+}
+
 void
 rb_removable_media_source_track_added (RBRemovableMediaSource *source,
 				       RhythmDBEntry *entry,
diff --git a/sources/rb-removable-media-source.h b/sources/rb-removable-media-source.h
index 763155b..3568205 100644
--- a/sources/rb-removable-media-source.h
+++ b/sources/rb-removable-media-source.h
@@ -62,8 +62,13 @@ typedef struct
 						 const char *uri,
 						 guint64 filesize,
 						 const char *mimetype);
+	gboolean	(*impl_should_paste)	(RBRemovableMediaSource *source,
+						 RhythmDBEntry *entry);
 } RBRemovableMediaSourceClass;
 
+typedef gboolean	(*RBRemovableMediaSourceShouldPasteFunc) (RBRemovableMediaSource *source,
+								  RhythmDBEntry *entry);
+
 GType			rb_removable_media_source_get_type	(void);
 
 char*		rb_removable_media_source_build_dest_uri 	(RBRemovableMediaSource *source,
@@ -76,6 +81,8 @@ void		rb_removable_media_source_track_added		(RBRemovableMediaSource *source,
 								 guint64 filesize,
 								 const char *mimetype);
 GList *		rb_removable_media_source_get_mime_types	(RBRemovableMediaSource *source);
+gboolean	rb_removable_media_source_should_paste		(RBRemovableMediaSource *source,
+								 RhythmDBEntry *entry);
 
 G_END_DECLS
 
