--- old-pie/bindings/python/rhythmdb.defs	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/bindings/python/rhythmdb.defs	2006-09-15 08:45:36.000000000 +1000
@@ -584,6 +579,36 @@
   (return-type "RhythmDBEntryType_*")
 )
 
+(define-method entry_request_extra_metadata
+  (of-object "RhythmDB")
+  (c-name "rhythmdb_entry_request_extra_metadata")
+  (return-type "GValue*")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+    '("const-gchar*" "property_name")
+  )
+)
+
+(define-method entry_gather_metadata
+  (of-object "RhythmDB")
+  (c-name "rhythmdb_entry_gather_metadata")
+  (return-type "GHashTable*")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+  )
+)
+
+(define-method emit_entry_extra_metadata_notify
+  (of-object "RhythmDB")
+  (c-name "rhythmdb_emit_entry_extra_metadata_notify")
+  (return-type "none")
+  (parameters
+    '("RhythmDBEntry*" "entry")
+    '("const-gchar*" "property_name")
+    '("const-GValue*" "metadata")
+  )
+)
+
 
 ;; From ../../rhythmdb/rhythmdb-property-model.h
 

--- old-pie/configure.ac	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/configure.ac	2006-09-15 08:45:36.000000000 +1000
@@ -898,6 +898,10 @@
 	AC_SUBST([PYGTK_CODEGEN])
 	AC_SUBST([PYGTK_H2DEF])
 
+dnl	uncomment when http://bugzilla.gnome.org/show_bug.cgi?id=351072 fixed
+dnl	PKG_CHECK_EXISTS([pygobject-2.0 >= X.XX.X],
+dnl			 AC_DEFINE([PYGOBJECT_CAN_MARSHAL_GVALUE]))
+
 	dnl Check for -fno-strict-aliasing
 	FLAGS="-fno-strict-aliasing"
 	save_CFLAGS="$CFLAGS"
--- old-pie/lib/rb-marshal.list	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/lib/rb-marshal.list	2006-09-15 08:45:36.000000000 +1000
@@ -4,8 +4,11 @@
 OBJECT:OBJECT
 STRING:STRING
 VOID:BOOLEAN,BOOLEAN
+BOXED:BOXED
+VOID:BOXED,BOXED
 VOID:BOXED,INT,POINTER,POINTER
 VOID:BOXED,POINTER
+VOID:BOXED,STRING,BOXED
 VOID:BOXED,ULONG
 VOID:DOUBLE,LONG
 VOID:INT,INT,DOUBLE

--- old-pie/plugins/rb-python-module.c	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/plugins/rb-python-module.c	2006-09-15 08:45:36.000000000 +1000
@@ -52,6 +52,35 @@
 	PROP_MODULE
 };
 
+#ifndef PYGOBJECT_CAN_MARSHAL_GVALUE
+static PyObject *
+pyg_value_g_value_as_pyobject (const GValue *value)
+{
+	return pyg_value_as_pyobject((GValue *)g_value_get_boxed(value), FALSE);
+}
+
+static int
+pyg_value_g_value_from_pyobject (GValue *value, PyObject *obj)
+{
+	GType type;
+	GValue obj_value = { 0, };
+	
+	type = pyg_type_from_object((PyObject *) obj->ob_type);
+	if (! type) {
+		PyErr_Clear();
+		return -1;
+	}
+	g_value_init(&obj_value, type);
+	if (pyg_value_from_pyobject(&obj_value, obj) == -1) {
+		g_value_unset(&obj_value);
+		return -1;
+	}
+	g_value_set_boxed(value, &obj_value);
+	g_value_unset(&obj_value);
+	return 0;
+}
+#endif /* PYGOBJECT_CAN_MARSHAL_GVALUE */
+
 /* Exported by pyrhythmdb module */
 void pyrhythmdb_register_classes (PyObject *d);
 void pyrhythmdb_add_constants (PyObject *module, const gchar *strip_prefix);
@@ -126,6 +155,10 @@
 	/* import gobject */
 	init_pygobject ();
 
+#ifndef PYGOBJECT_CAN_MARSHAL_GVALUE
+	pyg_register_gtype_custom (G_TYPE_VALUE, pyg_value_g_value_as_pyobject, pyg_value_g_value_from_pyobject);
+#endif
+
 	/* import gtk */
 	init_pygtk ();
 

--- old-pie/rhythmdb/rhythmdb.h	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/rhythmdb/rhythmdb.h	2006-09-15 08:45:36.000000000 +1000
@@ -254,6 +266,9 @@
 	void	(*entry_added)		(RhythmDB *db, RhythmDBEntry *entry);
 	void	(*entry_changed)	(RhythmDB *db, RhythmDBEntry *entry, GSList *changes); /* list of RhythmDBEntryChanges */
 	void	(*entry_deleted)	(RhythmDB *db, RhythmDBEntry *entry);
+	GValue *(*entry_extra_metadata_request) (RhythmDB *db, RhythmDBEntry *entry);
+	void    (*entry_extra_metadata_gather) (RhythmDB *db, RhythmDBEntry *entry, GHashTable *data);
+	void	(*entry_extra_metadata_notify) (RhythmDB *db, RhythmDBEntry *entry, const char *field, GValue *metadata);
 	void	(*load_complete)	(RhythmDB *db);
 	void	(*save_complete)	(RhythmDB *db);
 	void	(*load_error)		(RhythmDB *db, const char *uri, const char *msg);
@@ -394,6 +409,10 @@
 void		rhythmdb_emit_entry_added		(RhythmDB *db, RhythmDBEntry *entry);
 void		rhythmdb_emit_entry_deleted		(RhythmDB *db, RhythmDBEntry *entry);
 
+GValue *	rhythmdb_entry_request_extra_metadata	(RhythmDB *db, RhythmDBEntry *entry, const gchar *property_name);
+GHashTable *	rhythmdb_entry_gather_metadata		(RhythmDB *db, RhythmDBEntry *entry);
+void		rhythmdb_emit_entry_extra_metadata_notify (RhythmDB *db, RhythmDBEntry *entry, const gchar *property_name, const GValue *metadata);
+
 gboolean	rhythmdb_is_busy			(RhythmDB *db);
 char *		rhythmdb_compute_status_normal		(gint n_songs, glong duration,
 							 guint64 size,
--- old-pie/rhythmdb/rhythmdb.c	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/rhythmdb/rhythmdb.c	2006-09-15 08:45:36.000000000 +1000
@@ -116,6 +116,10 @@
 static void rhythmdb_entry_sync_mirrored (RhythmDBEntry *entry,
 					  guint propid);
 static void rhythmdb_register_core_entry_types (RhythmDB *db);
+static gboolean rhythmdb_entry_extra_metadata_accumulator (GSignalInvocationHint *ihint,
+							   GValue *return_accu,
+							   const GValue *handler_return,
+							   gpointer data);
 
 enum
 {
@@ -130,6 +134,9 @@
 	ENTRY_ADDED,
 	ENTRY_CHANGED,
 	ENTRY_DELETED,
+	ENTRY_EXTRA_METADATA_REQUEST,
+	ENTRY_EXTRA_METADATA_NOTIFY,
+	ENTRY_EXTRA_METADATA_GATHER,
 	LOAD_COMPLETE,
 	SAVE_COMPLETE,
 	SAVE_ERROR,
@@ -201,6 +208,36 @@
 			      G_TYPE_NONE, 2,
 			      RHYTHMDB_TYPE_ENTRY, G_TYPE_POINTER);
 
+	rhythmdb_signals[ENTRY_EXTRA_METADATA_REQUEST] =
+		g_signal_new ("entry_extra_metadata_request",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+			      G_STRUCT_OFFSET (RhythmDBClass, entry_extra_metadata_request),
+			      rhythmdb_entry_extra_metadata_accumulator, NULL,
+			      rb_marshal_BOXED__BOXED,
+			      G_TYPE_VALUE, 1,
+			      RHYTHMDB_TYPE_ENTRY);
+
+	rhythmdb_signals[ENTRY_EXTRA_METADATA_NOTIFY] =
+		g_signal_new ("entry_extra_metadata_notify",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+			      G_STRUCT_OFFSET (RhythmDBClass, entry_extra_metadata_notify),
+			      NULL, NULL,
+			      rb_marshal_VOID__BOXED_STRING_BOXED,
+			      G_TYPE_NONE, 3,
+			      RHYTHMDB_TYPE_ENTRY, G_TYPE_STRING, G_TYPE_VALUE);
+
+	rhythmdb_signals[ENTRY_EXTRA_METADATA_GATHER] =
+		g_signal_new ("entry_extra_metadata_gather",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RhythmDBClass, entry_extra_metadata_gather),
+			      NULL, NULL,
+			      rb_marshal_VOID__BOXED_BOXED,
+			      G_TYPE_NONE, 2,
+			      RHYTHMDB_TYPE_ENTRY, G_TYPE_HASH_TABLE);
+
 	rhythmdb_signals[LOAD_COMPLETE] =
 		g_signal_new ("load_complete",
 			      RHYTHMDB_TYPE,
@@ -3379,6 +3416,166 @@
 }
 
 static gboolean
+rhythmdb_entry_extra_metadata_accumulator (GSignalInvocationHint *ihint,
+					   GValue *return_accu,
+					   const GValue *handler_return,
+					   gpointer data)
+{
+	if (handler_return == NULL)
+		return TRUE;
+
+	g_value_copy (handler_return, return_accu);
+	return (g_value_get_boxed (return_accu) == NULL);
+}
+
+/**
+ * rhythmdb_entry_request_extra_metadata:
+ * @db: a #RhythmDB
+ * @entry: a #RhythmDBEntry
+ * @property_name: the metadata predicate
+ *
+ * Emits a request for extra metadata for the @entry.
+ * The @property_name argument is emitted as the ::detail part of the
+ * "entry_extra_metadata_request" signal. It should be a namespaced RDF
+ * predicate e.g. from Dublin Core, MusicBrainz, or internal to Rhythmbox
+ * (namespace "rb:"). Suitable predicates would be those that are expensive to
+ * acquire or only apply to a limited range of entries.
+ * Handlers capable of providing a particular predicate may ensure they only
+ * see appropriate requests by supplying an appropriate ::detail part when
+ * connecting to the signal. Upon a handler returning a non-%NULL value,
+ * emission will be stopped and the value returned to the caller; if no
+ * handlers return a non-%NULL value, the caller will receive %NULL. Priority
+ * is determined by signal connection order, with %G_CONNECT_AFTER providing a
+ * second, lower rank of priority.
+ * A handler returning a value should do so in a #GValue allocated on the heap;
+ * the accumulator will take ownership. The caller should unset and free the
+ * #GValue if non-%NULL when finished with it.
+ *
+ * Returns: an allocated, initialised, set #GValue, or NULL
+ **/
+GValue *
+rhythmdb_entry_request_extra_metadata (RhythmDB *db,
+				       RhythmDBEntry *entry,
+				       const gchar *property_name)
+{
+	GValue *value = NULL;
+
+	g_signal_emit (G_OBJECT (db),
+		       rhythmdb_signals[ENTRY_EXTRA_METADATA_REQUEST],
+		       g_quark_from_string (property_name),
+		       entry,
+		       &value);
+
+	return value;
+}
+
+/**
+ * rhythmdb_emit_entry_extra_metadata_notify:
+ * @db: a #RhythmDB
+ * @entry: a #RhythmDBEntry
+ * @property_name: the metadata predicate
+ * @metadata: a #GValue
+ *
+ * Emits a signal describing extra metadata for the @entry.  The @property_name
+ * argument is emitted as the ::detail part of the
+ * "entry_extra_metadata_notify" signal and as the 'field' parameter.  Handlers
+ * can ensure they only get metadata they are interested in by supplying an
+ * appropriate ::detail part when connecting to the signal.  If handlers are
+ * interested in the metadata they should ref or copy the contents of @metadata
+ * and unref or free it when they are finished with it.
+ **/
+void
+rhythmdb_emit_entry_extra_metadata_notify (RhythmDB *db,
+					   RhythmDBEntry *entry,
+					   const gchar *property_name,
+					   const GValue *metadata)
+{
+	g_signal_emit (G_OBJECT (db),
+		       rhythmdb_signals[ENTRY_EXTRA_METADATA_NOTIFY],
+		       g_quark_from_string (property_name),
+		       entry,
+		       property_name,
+		       metadata);
+}
+
+static void
+unset_and_free_g_value (gpointer valpointer)
+{
+	GValue *value = valpointer;
+	g_value_unset (value);
+	g_free (value);
+}
+
+/**
+ * rhythmdb_entry_extra_gather:
+ * @db: a #RhythmDB
+ * @entry: a #RhythmDBEntry
+ *
+ * Gathers all metadata for the @entry. The returned GHashTable maps property
+ * names and extra metadata names (described under
+ * @rhythmdb_entry_request_extra_metadata) to GValues. Anything wanting to
+ * provide extra metadata should connect to the "entry_extra_metadata_gather"
+ * signal.
+ *
+ * Returns: a GHashTable containing metadata for the entry.  This must be freed
+ * using g_hash_table_destroy.
+ */
+GHashTable *
+rhythmdb_entry_gather_metadata (RhythmDB *db,
+				RhythmDBEntry *entry)
+{
+	GHashTable *metadata;
+	GEnumClass *klass;
+	guint i;
+
+	metadata = g_hash_table_new_full (g_str_hash,
+					  g_str_equal,
+					  g_free,
+					  unset_and_free_g_value);
+
+	/* add core properties */
+	klass = g_type_class_ref (RHYTHMDB_TYPE_PROP_TYPE);
+	for (i = 0; i < klass->n_values; i++) {
+		GValue *value;
+		gint prop;
+		GType value_type;
+		const char *name;
+
+		prop = klass->values[i].value;
+
+		/* only include easily marshallable types in the hash table */
+		value_type = rhythmdb_get_property_type (db, prop);
+		switch (value_type) {
+		case G_TYPE_STRING:
+		case G_TYPE_BOOLEAN:
+		case G_TYPE_ULONG:
+		case G_TYPE_UINT64:
+		case G_TYPE_DOUBLE:
+			break;
+		default:
+			continue;
+		}
+
+		value = g_new0 (GValue, 1);
+		g_value_init (value, value_type);
+		rhythmdb_entry_get (db, entry, prop, value);
+		name = (char *)rhythmdb_nice_elt_name_from_propid (db, prop);
+		g_hash_table_insert (metadata,
+				     (gpointer) g_strdup (name),
+				     value);
+	}
+	g_type_class_unref (klass);
+
+	/* gather extra metadata */
+	g_signal_emit (G_OBJECT (db),
+		       rhythmdb_signals[ENTRY_EXTRA_METADATA_GATHER], 0,
+		       entry,
+		       metadata);
+
+	return metadata;
+}
+
+static gboolean
 queue_is_empty (GAsyncQueue *queue)
 {
 	return g_async_queue_length (queue) <= 0;
--- old-pie/shell/rb-shell.c	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/shell/rb-shell.c	2006-09-15 08:45:36.000000000 +1000
@@ -3189,14 +3164,6 @@
 	return TRUE;
 }
 
-static void
-unset_and_free_g_value (gpointer valpointer)
-{
-	GValue *value = valpointer;
-	g_value_unset (value);
-	g_free (value);
-}
-
 gboolean
 rb_shell_get_song_properties (RBShell *shell,
 			      const char *uri,
@@ -3204,8 +3171,6 @@
 			      GError **error)
 {
 	RhythmDBEntry *entry;
-	GEnumClass *klass;
-	guint i;
 
 	entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
 
@@ -3218,41 +3183,8 @@
 		return FALSE;
 	}
 
-	*properties = g_hash_table_new_full (g_str_hash, g_str_equal,
-					     NULL,
-					     unset_and_free_g_value);
-
-	klass = g_type_class_ref (RHYTHMDB_TYPE_PROP_TYPE);
-	for (i = 0; i < klass->n_values; i++) {
-		GValue *value;
-		gint prop;
-		GType value_type;
-
-		prop = klass->values[i].value;
-
-		/* only include easily marshallable types in the hash table */
-		value_type = rhythmdb_get_property_type (shell->priv->db, prop);
-		switch (value_type) {
-		case G_TYPE_STRING:
-		case G_TYPE_BOOLEAN:
-		case G_TYPE_ULONG:
-		case G_TYPE_UINT64:
-		case G_TYPE_DOUBLE:
-			break;
-		default:
-			continue;
-		}
-
-		value = g_new0 (GValue, 1);
-		g_value_init (value, value_type);
-		rhythmdb_entry_get (shell->priv->db, entry, prop, value);
-		g_hash_table_insert (*properties,
-				     (gpointer) rhythmdb_nice_elt_name_from_propid (shell->priv->db, prop),
-				     value);
-	}
-	g_type_class_unref (klass);
-
-	return TRUE;
+	*properties = rhythmdb_entry_gather_metadata (shell->priv->db, entry);
+	return (*properties != NULL);
 }
 
 gboolean

--- old-pie/shell/rb-shell-player.c	2006-09-15 08:45:35.000000000 +1000
+++ new-pie/shell/rb-shell-player.c	2006-09-15 08:45:36.000000000 +1000
@@ -151,6 +145,12 @@
 static void rb_shell_player_playing_changed_cb (RBShellPlayer *player,
 						GParamSpec *arg1,
 						gpointer user_data);
+static void rb_shell_player_extra_metadata_cb (RhythmDB *db,
+					       RhythmDBEntry *entry,
+					       const char *field,
+					       GValue *metadata,
+					       RBShellPlayer *player);
+
 
 static gboolean rb_shell_player_jump_to_current_idle (RBShellPlayer *player);
 
@@ -832,6 +811,9 @@
 		g_signal_handlers_disconnect_by_func (player->priv->db,
 						      G_CALLBACK (rb_shell_player_entry_changed_cb),
 						      player);
+		g_signal_handlers_disconnect_by_func (player->priv->db,
+						      G_CALLBACK (rb_shell_player_extra_metadata_cb),
+						      player);
 	}
 
 	player->priv->db = db;
@@ -842,6 +824,10 @@
 					 "entry_changed",
 					 G_CALLBACK (rb_shell_player_entry_changed_cb),
 					 player, 0);
+		g_signal_connect_object (G_OBJECT (player->priv->db),
+					 "entry_extra_metadata_notify",
+					 G_CALLBACK (rb_shell_player_extra_metadata_cb),
+					 player, 0);
 	}
 }
 
@@ -2312,6 +2293,46 @@
 }
 
 static void
+rb_shell_player_extra_metadata_cb (RhythmDB *db,
+				   RhythmDBEntry *entry,
+				   const char *field,
+				   GValue *metadata,
+				   RBShellPlayer *player)
+{
+
+	RhythmDBEntry *playing_entry;
+
+	playing_entry = rb_shell_player_get_playing_entry (player);
+	if (entry != playing_entry) {
+		if (playing_entry != NULL) {
+			rhythmdb_entry_unref (playing_entry);
+		}
+		return;
+	}
+
+	rb_shell_player_sync_with_source (player);
+
+	/* emit dbus signals for changes with easily marshallable types */
+	switch (G_VALUE_TYPE (metadata)) {
+	case G_TYPE_STRING:
+	case G_TYPE_BOOLEAN:
+	case G_TYPE_ULONG:
+	case G_TYPE_UINT64:
+	case G_TYPE_DOUBLE:
+		g_signal_emit (G_OBJECT (player),
+			       rb_shell_player_signals[PLAYING_SONG_PROPERTY_CHANGED], 0,
+			       rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION),
+			       field,
+			       metadata,		/* slightly silly */
+			       metadata);
+		break;
+	default:
+		break;
+	}
+}
+
+
+static void
 rb_shell_player_sync_with_source (RBShellPlayer *player)
 {
 	const char *entry_title = NULL;
