diff -rN -u old-pie3/bindings/python/rhythmdb.defs new-pie3/bindings/python/rhythmdb.defs
--- old-pie3/bindings/python/rhythmdb.defs	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/bindings/python/rhythmdb.defs	2006-07-09 12:24:30.000000000 +1000
@@ -539,7 +539,8 @@
   )
 )
 
-(define-function entry_register_type
+(define-method entry_register_type
+  (of-object "RhythmDB")
   (c-name "rhythmdb_entry_register_type")
   (return-type "RhythmDBEntryType_*")
   (parameters
diff -rN -u old-pie3/plugins/generic-player/rb-generic-player-source.c new-pie3/plugins/generic-player/rb-generic-player-source.c
--- old-pie3/plugins/generic-player/rb-generic-player-source.c	2006-07-09 12:24:30.000000000 +1000
+++ new-pie3/plugins/generic-player/rb-generic-player-source.c	2006-07-09 12:24:30.000000000 +1000
@@ -281,10 +281,13 @@
 {
 	RBGenericPlayerSource *source;
 	RhythmDBEntryType entry_type;
+	RhythmDB *db;
 
 	g_assert (rb_generic_player_is_volume_player (volume));
 
-	entry_type = rhythmdb_entry_register_type (NULL);
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	entry_type = rhythmdb_entry_register_type (db, NULL);
+	g_object_unref (G_OBJECT (db));
 
 	source = RB_GENERIC_PLAYER_SOURCE (g_object_new (RB_TYPE_GENERIC_PLAYER_SOURCE,
 					  "entry-type", entry_type,
diff -rN -u old-pie3/plugins/generic-player/rb-nokia770-source.c new-pie3/plugins/generic-player/rb-nokia770-source.c
--- old-pie3/plugins/generic-player/rb-nokia770-source.c	2006-07-09 12:24:30.000000000 +1000
+++ new-pie3/plugins/generic-player/rb-nokia770-source.c	2006-07-09 12:24:30.000000000 +1000
@@ -77,10 +77,13 @@
 {
 	RBNokia770Source *source;
 	RhythmDBEntryType entry_type;
+	RhythmDB *db;
 
 	g_assert (rb_nokia770_is_volume_player (volume));
 
-	entry_type =  rhythmdb_entry_register_type (NULL);
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	entry_type =  rhythmdb_entry_register_type (db, NULL);
+	g_object_unref (G_OBJECT (db));
 
 	source = RB_NOKIA770_SOURCE (g_object_new (RB_TYPE_NOKIA770_SOURCE,
 					  "entry-type", entry_type,
diff -rN -u old-pie3/plugins/generic-player/rb-psp-source.c new-pie3/plugins/generic-player/rb-psp-source.c
--- old-pie3/plugins/generic-player/rb-psp-source.c	2006-07-09 12:24:30.000000000 +1000
+++ new-pie3/plugins/generic-player/rb-psp-source.c	2006-07-09 12:24:30.000000000 +1000
@@ -81,10 +81,13 @@
 {
 	RBPspSource *source;
 	RhythmDBEntryType entry_type;
+	RhythmDB *db;
 
 	g_assert (rb_psp_is_volume_player (volume));
 
-	entry_type =  rhythmdb_entry_register_type (NULL);
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	entry_type =  rhythmdb_entry_register_type (db, NULL);
+	g_object_unref (G_OBJECT (db));
 
 	source = RB_PSP_SOURCE (g_object_new (RB_TYPE_PSP_SOURCE,
 					  "entry-type", entry_type,
diff -rN -u old-pie3/rhythmdb/rhythmdb.c new-pie3/rhythmdb/rhythmdb.c
--- old-pie3/rhythmdb/rhythmdb.c	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/rhythmdb/rhythmdb.c	2006-07-09 12:24:30.000000000 +1000
@@ -115,6 +115,7 @@
 static void rhythmdb_sync_library_location (RhythmDB *db);
 static void rhythmdb_entry_sync_mirrored (RhythmDBEntry *entry,
 					  guint propid);
+static void rhythmdb_register_core_entry_types (RhythmDB *db);
 
 enum
 {
@@ -381,15 +382,10 @@
 		g_hash_table_insert (db->priv->propname_map, (gpointer) name, GINT_TO_POINTER (i));
 	}
 
-	/* register core entry types */
-	rhythmdb_entry_song_get_type ();
-	rhythmdb_entry_ignore_get_type ();
-	rhythmdb_entry_import_error_get_type ();
-	
-	/* and other types we haven't kicked out of the rhythmdb core yet */
-	rhythmdb_entry_iradio_get_type ();
-	rhythmdb_entry_podcast_post_get_type ();
-	rhythmdb_entry_podcast_feed_get_type ();
+	db->priv->entry_type_map = g_hash_table_new (g_str_hash, g_str_equal);
+	db->priv->entry_type_map_mutex = g_mutex_new ();
+	db->priv->entry_type_mutex = g_mutex_new ();
+	rhythmdb_register_core_entry_types (db);
 
  	db->priv->stat_events = g_hash_table_new_full (gnome_vfs_uri_hash, (GEqualFunc) gnome_vfs_uri_equal,
  						       (GDestroyNotify) gnome_vfs_uri_unref,
@@ -3302,16 +3298,6 @@
 	return ret;
 }
 
-
-static gboolean
-song_can_sync_metadata (RhythmDB *db, RhythmDBEntry *entry, gpointer data)
-{
-	const char *mimetype;
-
-	mimetype = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);	
-	return rb_metadata_can_save (db->priv->metadata, mimetype);
-}
-
 static void
 default_sync_metadata (RhythmDB *db, RhythmDBEntry *entry, GError **error, gpointer data)
 {
@@ -3343,12 +3329,10 @@
 	}
 }
 
-static GHashTable *entry_type_map = NULL;
-static GStaticMutex entry_type_map_mutex = G_STATIC_MUTEX_INIT;
-static GStaticMutex entry_type_mutex = G_STATIC_MUTEX_INIT;
 
 /**
  * rhythmdb_entry_register_type:
+ * @db: a #RhythmDB
  * @name: optional name for the entry type
  *
  * Registers a new #RhythmDBEntryType. This should be called to create a new
@@ -3357,39 +3341,84 @@
  * Returns: the new #RhythmDBEntryType.
  **/
 RhythmDBEntryType
-rhythmdb_entry_register_type (const char *name)
+rhythmdb_entry_register_type (RhythmDB *db, const char *name)
 {
 	RhythmDBEntryType type;
+	RhythmDBClass *klass = RHYTHMDB_GET_CLASS (db);
 
 	type = g_new0 (RhythmDBEntryType_, 1);
 	type->can_sync_metadata = (RhythmDBEntryCanSyncFunc)rb_false_function;
 	type->sync_metadata = default_sync_metadata;
 	if (name) {
 		type->name = g_strdup (name);
-		g_static_mutex_lock (&entry_type_map_mutex);
-		if (entry_type_map == NULL) {
-			entry_type_map = g_hash_table_new (g_str_hash, g_str_equal);
-		}
-		g_hash_table_insert (entry_type_map, type->name, type);
-		g_static_mutex_unlock (&entry_type_map_mutex);
+		g_mutex_lock (db->priv->entry_type_map_mutex);
+		g_hash_table_insert (db->priv->entry_type_map, type->name, type);
+		g_mutex_unlock (db->priv->entry_type_map_mutex);
 	}
 
+	if (klass->impl_entry_type_registered)
+		klass->impl_entry_type_registered (db, name, type);
+
 	return type;
 }
 
 static void
-rhythmdb_entry_register_type_alias (RhythmDBEntryType type, const char *name)
+rhythmdb_entry_register_type_alias (RhythmDB *db, 
+				    RhythmDBEntryType type, 
+				    const char *name)
 {
 	/* yes, we leak the string */
 	char *dn = g_strdup (name);
 
-	g_static_mutex_lock (&entry_type_map_mutex);
-	g_hash_table_insert (entry_type_map, dn, type);
-	g_static_mutex_unlock (&entry_type_map_mutex);
+	g_mutex_lock (db->priv->entry_type_map_mutex);
+	g_hash_table_insert (db->priv->entry_type_map, dn, type);
+	g_mutex_unlock (db->priv->entry_type_map_mutex);
+}
+
+typedef struct {
+	GHFunc func;
+	gpointer data;
+} RhythmDBEntryTypeForeachData;
+
+static void
+rhythmdb_entry_type_foreach_cb (const char *name,
+				RhythmDBEntryType entry_type,
+				RhythmDBEntryTypeForeachData *data)
+{
+	/* skip aliases */
+	if (strcmp (entry_type->name, name))
+		return;
+
+	data->func ((gpointer) name, entry_type, data->data);
 }
 
 /**
+ * rhythmdb_entry_type_foreach:
+ * @db: a #RhythmDB
+ * @func: callback function to call for each registered entry type
+ * @data: data to pass to the callback
+ *
+ * Calls a function for each registered entry type.
+ */
+void
+rhythmdb_entry_type_foreach (RhythmDB *db, GHFunc func, gpointer data)
+{
+	RhythmDBEntryTypeForeachData d;
+
+	d.func = func;
+	d.data = data;
+
+	g_mutex_lock (db->priv->entry_type_mutex);
+	g_hash_table_foreach (db->priv->entry_type_map,
+			      (GHFunc) rhythmdb_entry_type_foreach_cb, 
+			      &d);
+	g_mutex_unlock (db->priv->entry_type_mutex);
+}
+
+
+/**
  * rhythmdb_entry_type_get_by_name:
+ * @db: a #RhythmDB
  * @name: name of the type to look for
  *
  * Locates a #RhythmDBEntryType by name. Returns 
@@ -3399,15 +3428,15 @@
  * Returns: the #RhythmDBEntryType
  */
 RhythmDBEntryType
-rhythmdb_entry_type_get_by_name (const char *name)
+rhythmdb_entry_type_get_by_name (RhythmDB *db, const char *name)
 {
 	gpointer t = NULL;
 
-	g_static_mutex_lock (&entry_type_map_mutex);
-	if (entry_type_map) {
-		t = g_hash_table_lookup (entry_type_map, name);
+	g_mutex_lock (db->priv->entry_type_map_mutex);
+	if (db->priv->entry_type_map) {
+		t = g_hash_table_lookup (db->priv->entry_type_map, name);
 	}
-	g_static_mutex_unlock (&entry_type_map_mutex);
+	g_mutex_unlock (db->priv->entry_type_map_mutex);
 
 	if (t)
 		return (RhythmDBEntryType) t;
@@ -3415,61 +3444,25 @@
 	return RHYTHMDB_ENTRY_TYPE_INVALID;
 }
 
-
-RhythmDBEntryType 
-rhythmdb_entry_song_get_type (void) 
-{
-	static RhythmDBEntryType song_type = RHYTHMDB_ENTRY_TYPE_INVALID;
-
-	g_static_mutex_lock (&entry_type_mutex);
-	if (song_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
-		song_type = rhythmdb_entry_register_type ("song");
-		rhythmdb_entry_register_type_alias (song_type, "0");
-		song_type->can_sync_metadata = song_can_sync_metadata;
-	}
-	g_static_mutex_unlock (&entry_type_mutex);
-
-	return song_type;
-}
-
-RhythmDBEntryType 
-rhythmdb_entry_ignore_get_type (void) 
+static gboolean
+song_can_sync_metadata (RhythmDB *db, RhythmDBEntry *entry, gpointer data)
 {
-	static RhythmDBEntryType ignore_type = RHYTHMDB_ENTRY_TYPE_INVALID;
-
-	g_static_mutex_lock (&entry_type_mutex);
-	if (ignore_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
-		ignore_type = rhythmdb_entry_register_type ("ignore");
-	}
-	g_static_mutex_unlock (&entry_type_mutex);
+	const char *mimetype;
 
-	return ignore_type;
+	mimetype = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);	
+	return rb_metadata_can_save (db->priv->metadata, mimetype);
 }
 
-RhythmDBEntryType rhythmdb_entry_iradio_get_type (void) 
-{
-	static RhythmDBEntryType iradio_type = RHYTHMDB_ENTRY_TYPE_INVALID;
-       
-	g_static_mutex_lock (&entry_type_mutex);
-	if (iradio_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
-		iradio_type = rhythmdb_entry_register_type ("iradio");
-		rhythmdb_entry_register_type_alias (iradio_type, "1");
-		iradio_type->can_sync_metadata = (RhythmDBEntryCanSyncFunc)rb_true_function;
-		iradio_type->sync_metadata = (RhythmDBEntrySyncFunc)rb_null_function;
-	}
-	g_static_mutex_unlock (&entry_type_mutex);
-
-	return iradio_type;
-}
 
 static char *
-_get_podcast_playback_uri (RhythmDBEntry *entry, gpointer data)
+podcast_get_playback_uri (RhythmDBEntry *entry, gpointer data)
 {
 	return rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_MOUNTPOINT);
 }
 
+
 static void
-_podcast_data_destroy (RhythmDBEntry *entry, gpointer something)
+podcast_data_destroy (RhythmDBEntry *entry, gpointer something)
 {
 	RhythmDBPodcastFields *podcast = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RhythmDBPodcastFields);
 	rb_refstring_unref (podcast->description);
@@ -3480,56 +3473,91 @@
 	rb_refstring_unref (podcast->image);
 }
 
-RhythmDBEntryType rhythmdb_entry_podcast_post_get_type (void) 
-{
-	static RhythmDBEntryType podcast_post_type = RHYTHMDB_ENTRY_TYPE_INVALID;
-       
-	g_static_mutex_lock (&entry_type_mutex);
-	if (podcast_post_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
-		podcast_post_type = rhythmdb_entry_register_type ("podcast-post");
-		rhythmdb_entry_register_type_alias (podcast_post_type, "2");
-
-		podcast_post_type->entry_type_data_size = sizeof (RhythmDBPodcastFields);
-		podcast_post_type->pre_entry_destroy = (RhythmDBEntryActionFunc) _podcast_data_destroy;
-		podcast_post_type->get_playback_uri = _get_podcast_playback_uri;
-	}
-	g_static_mutex_unlock (&entry_type_mutex);
+static RhythmDBEntryType song_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+static RhythmDBEntryType ignore_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+static RhythmDBEntryType import_error_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+
+/* to be evicted */
+static RhythmDBEntryType iradio_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+static RhythmDBEntryType podcast_post_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+static RhythmDBEntryType podcast_feed_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+
+static void
+rhythmdb_register_core_entry_types (RhythmDB *db)
+{
+	/* regular songs */
+	song_type = rhythmdb_entry_register_type (db, "song");
+	rhythmdb_entry_register_type_alias (db, song_type, "0");
+	song_type->save_to_disk = TRUE;
+	song_type->can_sync_metadata = song_can_sync_metadata;
+
+	/* import errors */
+	import_error_type = rhythmdb_entry_register_type (db, "import-error");
+	import_error_type->get_playback_uri = (RhythmDBEntryStringFunc)rb_null_function;
+
+	/* ignored files */
+	ignore_type = rhythmdb_entry_register_type (db, "ignore");
+	ignore_type->save_to_disk = TRUE;
+
+	/* iradio streams */
+	iradio_type = rhythmdb_entry_register_type (db, "iradio");
+	iradio_type->save_to_disk = TRUE;
+	iradio_type->can_sync_metadata = (RhythmDBEntryCanSyncFunc)rb_true_function;
+	iradio_type->sync_metadata = (RhythmDBEntrySyncFunc)rb_null_function;
+
+	/* podcast posts */
+	podcast_post_type = rhythmdb_entry_register_type (db, "podcast-post");
+	podcast_post_type->entry_type_data_size = sizeof (RhythmDBPodcastFields);
+	podcast_post_type->save_to_disk = TRUE;
+	podcast_post_type->pre_entry_destroy = (RhythmDBEntryActionFunc) podcast_data_destroy;
+	podcast_post_type->get_playback_uri = podcast_get_playback_uri;
+
+	/* podcast feeds */
+	podcast_feed_type = rhythmdb_entry_register_type (db, "podcast-feed");
+	podcast_feed_type->entry_type_data_size = sizeof (RhythmDBPodcastFields);
+	podcast_feed_type->save_to_disk = TRUE;
+	podcast_feed_type->pre_entry_destroy = (RhythmDBEntryActionFunc) podcast_data_destroy;
+}
 
-	return podcast_post_type;
+RhythmDBEntryType 
+rhythmdb_entry_song_get_type (void) 
+{
+	return song_type;
 }
 
-RhythmDBEntryType rhythmdb_entry_podcast_feed_get_type (void) 
+RhythmDBEntryType 
+rhythmdb_entry_ignore_get_type (void) 
 {
-	static RhythmDBEntryType podcast_feed_type = RHYTHMDB_ENTRY_TYPE_INVALID;
-       
-	g_static_mutex_lock (&entry_type_mutex);
-	if (podcast_feed_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
-		podcast_feed_type = rhythmdb_entry_register_type ("podcast-feed");
-		rhythmdb_entry_register_type_alias (podcast_feed_type, "3");
+	return ignore_type;
+}
 
-		podcast_feed_type->entry_type_data_size = sizeof (RhythmDBPodcastFields);
-		podcast_feed_type->pre_entry_destroy = (RhythmDBEntryActionFunc) _podcast_data_destroy;
-	}
-	g_static_mutex_unlock (&entry_type_mutex);
+RhythmDBEntryType 
+rhythmdb_entry_import_error_get_type (void) 
+{
+	return import_error_type;
+}
 
-	return podcast_feed_type;
+RhythmDBEntryType
+rhythmdb_entry_iradio_get_type (void)
+{
+	return iradio_type;
 }
 
-RhythmDBEntryType rhythmdb_entry_import_error_get_type (void) 
+RhythmDBEntryType 
+rhythmdb_entry_podcast_post_get_type (void) 
 {
-	static RhythmDBEntryType import_error_type = RHYTHMDB_ENTRY_TYPE_INVALID;
-       
-	g_static_mutex_lock (&entry_type_mutex);
-	if (import_error_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
-		import_error_type = rhythmdb_entry_register_type ("import-error");
-		
-		import_error_type->get_playback_uri = (RhythmDBEntryStringFunc)rb_null_function;
-	}
-	g_static_mutex_unlock (&entry_type_mutex);
+	return podcast_post_type;
+}
 
-	return import_error_type;
+RhythmDBEntryType 
+rhythmdb_entry_podcast_feed_get_type (void) 
+{
+	return podcast_feed_type;
 }
 
+
+
+
 static void
 rhythmdb_entry_set_mount_point (RhythmDB *db, RhythmDBEntry *entry, 
 				const gchar *realuri)
diff -rN -u old-pie3/rhythmdb/rhythmdb.h new-pie3/rhythmdb/rhythmdb.h
--- old-pie3/rhythmdb/rhythmdb.h	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/rhythmdb/rhythmdb.h	2006-07-09 12:24:30.000000000 +1000
@@ -60,6 +60,7 @@
 	char 				*name;
 
 	guint				entry_type_data_size;
+	gboolean			save_to_disk;
 
 	/* virtual functions here */
 	RhythmDBEntryActionFunc		post_entry_create;
@@ -289,6 +290,10 @@
 	void		(*impl_do_full_query)	(RhythmDB *db, GPtrArray *query,
 						 RhythmDBQueryResults *results,
 						 gboolean *cancel);
+
+	void		(*impl_entry_type_registered) (RhythmDB *db, 
+						       const char *name, 
+						       RhythmDBEntryType type);
 } RhythmDBClass;
 
 
@@ -400,8 +405,8 @@
 							 const char *plural);
 
 
-RhythmDBEntryType rhythmdb_entry_register_type          (const char *name);
-RhythmDBEntryType rhythmdb_entry_type_get_by_name       (const char *name);
+RhythmDBEntryType rhythmdb_entry_register_type          (RhythmDB *db, const char *name);
+RhythmDBEntryType rhythmdb_entry_type_get_by_name       (RhythmDB *db, const char *name);
 
 RhythmDBEntryType rhythmdb_entry_song_get_type          (void);
 RhythmDBEntryType rhythmdb_entry_iradio_get_type        (void);
diff -rN -u old-pie3/rhythmdb/rhythmdb-private.h new-pie3/rhythmdb/rhythmdb-private.h
--- old-pie3/rhythmdb/rhythmdb-private.h	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/rhythmdb/rhythmdb-private.h	2006-07-09 12:24:30.000000000 +1000
@@ -168,6 +168,10 @@
 
 	gboolean saving;
 	gboolean dirty;
+
+	GHashTable *entry_type_map;
+	GMutex *entry_type_map_mutex;
+	GMutex *entry_type_mutex;
 };
 
 typedef struct
@@ -211,6 +215,7 @@
 void rhythmdb_entry_set_internal (RhythmDB *db, RhythmDBEntry *entry, 
 				  gboolean notify_if_inserted, guint propid, 
 				  const GValue *value);
+void rhythmdb_entry_type_foreach (RhythmDB *db, GHFunc func, gpointer data);
 
 /* from rhythmdb-monitor.c */
 void rhythmdb_init_monitoring (RhythmDB *db);
diff -rN -u old-pie3/rhythmdb/rhythmdb-query.c new-pie3/rhythmdb/rhythmdb-query.c
--- old-pie3/rhythmdb/rhythmdb-query.c	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/rhythmdb/rhythmdb-query.c	2006-07-09 12:24:30.000000000 +1000
@@ -360,7 +360,7 @@
 	case G_TYPE_POINTER:
 		if (propid == RHYTHMDB_PROP_TYPE) {
 			RhythmDBEntryType entry_type;
-			entry_type = rhythmdb_entry_type_get_by_name (content);
+			entry_type = rhythmdb_entry_type_get_by_name (db, content);
 			if (entry_type != RHYTHMDB_ENTRY_TYPE_INVALID) {
 				g_value_set_pointer (val, entry_type);
 				break;
diff -rN -u old-pie3/rhythmdb/rhythmdb-tree.c new-pie3/rhythmdb/rhythmdb-tree.c
--- old-pie3/rhythmdb/rhythmdb-tree.c	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/rhythmdb/rhythmdb-tree.c	2006-07-09 12:24:30.000000000 +1000
@@ -78,6 +78,9 @@
 					 gboolean *cancel);
 static gboolean rhythmdb_tree_evaluate_query (RhythmDB *adb, GPtrArray *query,
 				       RhythmDBEntry *aentry);
+static void rhythmdb_tree_entry_type_registered (RhythmDB *db,
+						 const char *name,
+						 RhythmDBEntryType type);
 
 typedef void (*RBTreeEntryItFunc)(RhythmDBTree *db, 
 				  RhythmDBEntry *entry, 
@@ -115,12 +118,25 @@
 {
 	GHashTable *entries;
 	GHashTable *genres;
+	GHashTable *unknown_entry_types;
 	GMutex *genres_lock;
 	gboolean finalizing;
 
 	guint idle_load_id;
 };
 
+typedef struct 
+{
+	char *name;
+	char *value;
+} RhythmDBUnknownEntryProperty;
+
+typedef struct 
+{
+	char *typename;
+	GList *properties;
+} RhythmDBUnknownEntry;
+
 #define RHYTHMDB_TREE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RHYTHMDB_TYPE_TREE, RhythmDBTreePrivate))
 
 enum
@@ -149,6 +165,7 @@
 	rhythmdb_class->impl_entry_foreach = rhythmdb_tree_entry_foreach;
 	rhythmdb_class->impl_evaluate_query = rhythmdb_tree_evaluate_query;
 	rhythmdb_class->impl_do_full_query = rhythmdb_tree_do_full_query;
+	rhythmdb_class->impl_entry_type_registered = rhythmdb_tree_entry_type_registered;
 
 	g_type_class_add_private (klass, sizeof (RhythmDBTreePrivate));
 }
@@ -162,6 +179,7 @@
 
 	db->priv->genres = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 						  NULL, (GDestroyNotify)g_hash_table_destroy);
+	db->priv->unknown_entry_types = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 static void
@@ -171,6 +189,32 @@
 }
 
 static void
+free_unknown_entries (const char *name,
+		      GList *entries,
+		      gpointer nah)
+{
+	GList *e;
+	for (e = entries; e != NULL; e = e->next) {
+		RhythmDBUnknownEntry *entry;
+		GList *p;
+
+		entry = (RhythmDBUnknownEntry *)e->data;
+		g_free (entry->typename);
+		for (p = entry->properties; p != NULL; p = p->next) {
+			RhythmDBUnknownEntryProperty *prop;
+
+			prop = (RhythmDBUnknownEntryProperty *)p->data;
+			g_free (prop->name);
+			g_free (prop->value);
+			g_free (prop);
+		}
+
+		g_list_free (entry->properties);
+	}
+	g_list_free (entries);
+}
+
+static void
 rhythmdb_tree_finalize (GObject *object)
 {
 	RhythmDBTree *db;
@@ -190,6 +234,11 @@
 
 	g_hash_table_destroy (db->priv->genres);
 
+	g_hash_table_foreach (db->priv->unknown_entry_types, 
+			      (GHFunc) free_unknown_entries, 
+			      NULL);
+	g_hash_table_destroy (db->priv->unknown_entry_types);
+
 	G_OBJECT_CLASS (rhythmdb_tree_parent_class)->finalize (object);
 }
 
@@ -203,10 +252,13 @@
 		RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB,
 		RHYTHMDB_TREE_PARSER_STATE_ENTRY,
 		RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY,
+		RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY,
+		RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY_PROPERTY,
 		RHYTHMDB_TREE_PARSER_STATE_END,
 	} state;
-	gboolean in_unknown_elt;
+	guint in_unknown_elt;
 	RhythmDBEntry *entry;
+	RhythmDBUnknownEntry *unknown_entry;
 	GString *buf;
 	RhythmDBPropType propid;
 	gint batch_count;
@@ -226,8 +278,10 @@
 		return;
 	}
 
-	if (ctx->in_unknown_elt)
+	if (ctx->in_unknown_elt) {
+		ctx->in_unknown_elt++;
 		return;
+	}
 
 	switch (ctx->state)
 	{
@@ -258,7 +312,7 @@
 			}
 
 		} else {
-			ctx->in_unknown_elt = TRUE;
+			ctx->in_unknown_elt++;
 		}
 
 		break;
@@ -267,31 +321,37 @@
 	{
 		if (!strcmp (name, "entry")) {
 			RhythmDBEntryType type = RHYTHMDB_ENTRY_TYPE_INVALID;
-			gboolean type_set = FALSE;
+			const char *typename = NULL;
 			for (; *attrs; attrs +=2) {
 				if (!strcmp (*attrs, "type")) {
-					const char *typename = *(attrs+1);
-					type = rhythmdb_entry_type_get_by_name (typename);
-					if (!type)
-						return;
-					type_set = TRUE;
+					typename = *(attrs+1);
+					type = rhythmdb_entry_type_get_by_name (RHYTHMDB (ctx->db), typename);
 					break;
 				}
 			}
-			g_assert (type_set);
-			ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
-			ctx->entry = rhythmdb_entry_allocate (RHYTHMDB (ctx->db), type);
-			ctx->entry->flags |= RHYTHMDB_ENTRY_TREE_LOADING;
-			ctx->has_date = FALSE;
-		} else
-			ctx->in_unknown_elt = TRUE;
+
+			g_assert (typename);
+			if (type != RHYTHMDB_ENTRY_TYPE_INVALID) {
+				ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
+				ctx->entry = rhythmdb_entry_allocate (RHYTHMDB (ctx->db), type);
+				ctx->entry->flags |= RHYTHMDB_ENTRY_TREE_LOADING;
+				ctx->has_date = FALSE;
+			} else {
+				rb_debug ("reading unknown entry");
+				ctx->state = RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY;
+				ctx->unknown_entry = g_new0 (RhythmDBUnknownEntry, 1);
+				ctx->unknown_entry->typename = g_strdup (typename);
+			}
+		} else {
+			ctx->in_unknown_elt++;
+		}
 		break;
 	}
 	case RHYTHMDB_TREE_PARSER_STATE_ENTRY:
 	{
 		int val = rhythmdb_propid_from_nice_elt_name (RHYTHMDB (ctx->db), BAD_CAST name);
 		if (val < 0) {
-			ctx->in_unknown_elt = TRUE;
+			ctx->in_unknown_elt++;
 			break;
 		}
 		
@@ -300,6 +360,19 @@
 		g_string_truncate (ctx->buf, 0);
 		break;
 	}
+	case RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY:
+	{
+		RhythmDBUnknownEntryProperty *prop;
+		
+		prop = g_new0 (RhythmDBUnknownEntryProperty, 1);
+		prop->name = g_strdup (name);
+
+		ctx->unknown_entry->properties = g_list_prepend (ctx->unknown_entry->properties, prop);
+		ctx->state = RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY_PROPERTY;
+		g_string_truncate (ctx->buf, 0);
+		break;
+	}
+	case RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY_PROPERTY:
 	case RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY:
 	case RHYTHMDB_TREE_PARSER_STATE_END:
 	break;
@@ -315,7 +388,7 @@
 	}
 
 	if (ctx->in_unknown_elt) {
-		ctx->in_unknown_elt = FALSE;		
+		ctx->in_unknown_elt--;
 		return;
 	}
 	
@@ -381,6 +454,22 @@
 			rhythmdb_entry_unref (ctx->entry);
 		}
 		ctx->state = RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB;
+		ctx->entry = NULL;
+		break;
+	}
+	case RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY:
+	{
+		GList *entry_list;
+
+		rb_debug ("finished reading unknown entry");
+		ctx->unknown_entry->properties = g_list_reverse (ctx->unknown_entry->properties);
+
+		entry_list = g_hash_table_lookup (ctx->db->priv->unknown_entry_types, ctx->unknown_entry->typename);
+		entry_list = g_list_prepend (entry_list, ctx->unknown_entry);
+		g_hash_table_insert (ctx->db->priv->unknown_entry_types, ctx->unknown_entry->typename, entry_list);
+
+		ctx->state = RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB;
+		ctx->unknown_entry = NULL;
 		break;
 	}
 	case RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY:
@@ -424,6 +513,19 @@
 		ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
 		break;
 	}
+	case RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY_PROPERTY:
+	{
+		RhythmDBUnknownEntryProperty *prop;
+		
+		g_assert (ctx->unknown_entry->properties);
+		prop = ctx->unknown_entry->properties->data;
+		g_assert (prop->value == NULL);
+		prop->value = g_strdup (ctx->buf->str);
+		rb_debug ("unknown entry property: %s = %s", prop->name, prop->value);
+
+		ctx->state = RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY;
+		break;
+	}
 	case RHYTHMDB_TREE_PARSER_STATE_START:
 	case RHYTHMDB_TREE_PARSER_STATE_END:
 	break;
@@ -442,9 +544,11 @@
 	switch (ctx->state)
 	{
 	case RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY:
+	case RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY_PROPERTY:
 		g_string_append_len (ctx->buf, data, len);
 		break;
 	case RHYTHMDB_TREE_PARSER_STATE_ENTRY:
+	case RHYTHMDB_TREE_PARSER_STATE_UNKNOWN_ENTRY:
 	case RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB:
 	case RHYTHMDB_TREE_PARSER_STATE_START:
 	case RHYTHMDB_TREE_PARSER_STATE_END:
@@ -626,6 +730,7 @@
 {
 	RhythmDBPropType i;
 	RhythmDBPodcastFields *podcast = NULL;
+	xmlChar *encoded;
 
 	if (ctx->error)
 		return;
@@ -635,20 +740,9 @@
 		podcast = RHYTHMDB_ENTRY_GET_TYPE_DATA (entry, RhythmDBPodcastFields);
 
 	RHYTHMDB_FWRITE_STATICSTR ("  <entry type=\"", ctx->handle, ctx->error);
-
-	if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
-		RHYTHMDB_FWRITE_STATICSTR ("song", ctx->handle, ctx->error);
-	} else if (entry->type == RHYTHMDB_ENTRY_TYPE_IRADIO_STATION) {
-		RHYTHMDB_FWRITE_STATICSTR ("iradio", ctx->handle, ctx->error);
-	} else if (entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_POST) {
-		RHYTHMDB_FWRITE_STATICSTR ("podcast-post", ctx->handle, ctx->error);
-	} else if (entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
-		RHYTHMDB_FWRITE_STATICSTR ("podcast-feed", ctx->handle, ctx->error);
-	} else if (entry->type == RHYTHMDB_ENTRY_TYPE_IGNORE) {
-		RHYTHMDB_FWRITE_STATICSTR ("ignore", ctx->handle, ctx->error);
-	} else {
-		g_assert_not_reached ();
-	}
+	encoded	= xmlEncodeEntitiesReentrant (NULL, BAD_CAST entry->type->name);
+	RHYTHMDB_FWRITE (encoded, 1, xmlStrlen (encoded), ctx->handle, ctx->error);
+	g_free (encoded);
 
 	RHYTHMDB_FWRITE_STATICSTR ("\">\n", ctx->handle, ctx->error);
 		
@@ -805,6 +899,54 @@
 }
 
 static void
+save_entry_type (const char *name, 
+		 RhythmDBEntryType entry_type, 
+		 struct RhythmDBTreeSaveContext *ctx)
+{
+	if (entry_type->save_to_disk == FALSE)
+		return;
+
+	rb_debug ("saving entries of type %s", name);
+	rhythmdb_hash_tree_foreach (RHYTHMDB (ctx->db), entry_type, 
+				    (RBTreeEntryItFunc) save_entry,
+				    NULL, NULL, NULL, ctx);
+}
+
+static void
+save_unknown_entry_type (const char *typename,
+			 GList *entries,
+			 struct RhythmDBTreeSaveContext *ctx)
+{
+	GList *t;
+
+	for (t = entries; t != NULL; t = t->next) {
+		RhythmDBUnknownEntry *entry;
+		xmlChar *encoded;
+		GList *p;
+
+		if (ctx->error)
+			return;
+		
+		entry = (RhythmDBUnknownEntry *)t->data;
+
+		RHYTHMDB_FWRITE_STATICSTR ("  <entry type=\"", ctx->handle, ctx->error);
+		encoded	= xmlEncodeEntitiesReentrant (NULL, BAD_CAST entry->typename);
+		RHYTHMDB_FWRITE (encoded, 1, xmlStrlen (encoded), ctx->handle, ctx->error);
+		g_free (encoded);
+
+		RHYTHMDB_FWRITE_STATICSTR ("\">\n", ctx->handle, ctx->error);
+
+		for (p = entry->properties; p != NULL; p = p->next) {
+			RhythmDBUnknownEntryProperty *prop;
+			prop = (RhythmDBUnknownEntryProperty *) p->data;
+			save_entry_string(ctx, (const xmlChar *)prop->name, prop->value);
+		}
+		
+		RHYTHMDB_FWRITE_STATICSTR ("  </entry>\n", ctx->handle, ctx->error);
+	}
+}
+
+static void
 rhythmdb_tree_save (RhythmDB *rdb)
 {
 	RhythmDBTree *db = RHYTHMDB_TREE (rdb);
@@ -832,21 +974,10 @@
 				   "<rhythmdb version=\"" RHYTHMDB_TREE_XML_VERSION "\">\n", 
 				   ctx.handle, ctx.error);
 
-	rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_SONG,
-				    (RBTreeEntryItFunc)save_entry,
-				    NULL, NULL, NULL, &ctx);
-	rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_IRADIO_STATION,
-				    (RBTreeEntryItFunc)save_entry,
-				    NULL, NULL, NULL, &ctx);
-	rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_POST,
-				    (RBTreeEntryItFunc)save_entry,
-				    NULL, NULL, NULL, &ctx);
-	rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED,
-				    (RBTreeEntryItFunc)save_entry,
-				    NULL, NULL, NULL, &ctx);
-	rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_IGNORE,
-				    (RBTreeEntryItFunc)save_entry,
-				    NULL, NULL, NULL, &ctx);
+	rhythmdb_entry_type_foreach (rdb, (GHFunc) save_entry_type, &ctx);
+	g_hash_table_foreach (db->priv->unknown_entry_types, 
+			      (GHFunc) save_unknown_entry_type, 
+			      &ctx);
 
 	RHYTHMDB_FWRITE_STATICSTR ("</rhythmdb>\n", ctx.handle, ctx.error);
 
@@ -1978,3 +2109,54 @@
 		g_hash_table_foreach (table, hash_tree_genres_foreach, &ctxt);
 	}
 }
+
+static void
+rhythmdb_tree_entry_type_registered (RhythmDB *db, 
+				     const char *name, 
+				     RhythmDBEntryType entry_type)
+{
+	GList *entries;
+	GList *e;
+	gint count = 0;
+	RhythmDBTree *rdb;
+	if (name == NULL)
+		return;
+
+	rdb = RHYTHMDB_TREE (db);
+	entries = g_hash_table_lookup (rdb->priv->unknown_entry_types, name);
+	if (entries == NULL) {
+		rb_debug ("no entries of newly registered type %s loaded from db", name);
+		return;
+	}
+
+	for (e = entries; e != NULL; e = e->next) {
+		RhythmDBUnknownEntry *data;
+		RhythmDBEntry *entry;
+		GList *p;
+
+		data = (RhythmDBUnknownEntry *)e->data;
+		entry = rhythmdb_entry_allocate (db, entry_type);
+		entry->flags |= RHYTHMDB_ENTRY_TREE_LOADING;
+		for (p = data->properties; p != NULL; p = p->next) {
+			RhythmDBUnknownEntryProperty *prop;
+			RhythmDBPropType propid;
+			GValue value = {0,};
+
+			prop = (RhythmDBUnknownEntryProperty *) p->data;
+			propid = rhythmdb_propid_from_nice_elt_name (db, (const xmlChar *) prop->name);
+
+			rhythmdb_read_encoded_property (db, prop->value, propid, &value);
+			rhythmdb_entry_set_internal (db, entry, FALSE, propid, &value);
+			g_value_unset (&value);
+		}
+		rhythmdb_tree_entry_new (db, entry);
+		rhythmdb_entry_insert (db, entry);
+		count++;
+	}
+	rb_debug ("handled %d entries of newly registered type %s", count, name);
+	rhythmdb_commit (db);
+	
+	g_hash_table_remove (rdb->priv->unknown_entry_types, name);
+	free_unknown_entries (name, entries, NULL);
+}
+
diff -rN -u old-pie3/sources/rb-audiocd-source.c new-pie3/sources/rb-audiocd-source.c
--- old-pie3/sources/rb-audiocd-source.c	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/sources/rb-audiocd-source.c	2006-07-09 12:24:30.000000000 +1000
@@ -164,10 +164,14 @@
 	char *device_path;
 	GObject *source;
 	RhythmDBEntryType entry_type;
+	RhythmDB *db;
 
 	g_assert (rb_audiocd_is_volume_audiocd (volume));
 
-	entry_type =  rhythmdb_entry_register_type (NULL);
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	entry_type =  rhythmdb_entry_register_type (db, NULL);
+	g_object_unref (G_OBJECT (db));
+
 	entry_type->can_sync_metadata = (RhythmDBEntryCanSyncFunc)rb_true_function;
 	/* TODO same the metadata somewhere */
 	entry_type->sync_metadata = (RhythmDBEntrySyncFunc)rb_null_function;
diff -rN -u old-pie3/sources/rb-daap-source.c new-pie3/sources/rb-daap-source.c
--- old-pie3/sources/rb-daap-source.c	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/sources/rb-daap-source.c	2006-07-09 12:24:30.000000000 +1000
@@ -117,12 +117,6 @@
 GtkActionGroup *daap_action_group;
 guint daap_ui_merge_id;
 
-static RhythmDBEntryType
-rhythmdb_entry_daap_type_new (void)
-{
-	return rhythmdb_entry_register_type (NULL);
-}
-
 static void
 rb_daap_source_class_init (RBDAAPSourceClass *klass)
 {
@@ -306,8 +300,11 @@
 	RBSource *source;
 	RhythmDBEntryType type;
 	GdkPixbuf *icon;
+	RhythmDB *db;
 
-	type = rhythmdb_entry_daap_type_new ();
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	type = rhythmdb_entry_register_type (db, NULL);
+	g_object_unref (G_OBJECT (db));
 
 	icon = rb_daap_get_icon (password_protected, FALSE);
 
diff -rN -u old-pie3/sources/rb-ipod-source.c new-pie3/sources/rb-ipod-source.c
--- old-pie3/sources/rb-ipod-source.c	2006-07-09 12:24:29.000000000 +1000
+++ new-pie3/sources/rb-ipod-source.c	2006-07-09 12:24:30.000000000 +1000
@@ -202,10 +202,13 @@
 {
 	RBiPodSource *source;
 	RhythmDBEntryType entry_type;
+	RhythmDB *db;
 
 	g_assert (rb_ipod_is_volume_ipod (volume));
 
-	entry_type =  rhythmdb_entry_register_type (NULL);
+	g_object_get (G_OBJECT (shell), "db", &db, NULL);
+	entry_type =  rhythmdb_entry_register_type (db, NULL);
+	g_object_unref (G_OBJECT (shell));
 
 	source = RB_IPOD_SOURCE (g_object_new (RB_TYPE_IPOD_SOURCE,
 					  "entry-type", entry_type,

