Blob Blame History Raw
diff --git a/backends/gstreamer/Makefile.am b/backends/gstreamer/Makefile.am
index 01cb788..093a02d 100644
--- a/backends/gstreamer/Makefile.am
+++ b/backends/gstreamer/Makefile.am
@@ -8,6 +8,7 @@ librbbackendsgstreamer_la_SOURCES =			\
 	$(NULL)
 
 librbbackendsgstreamer_la_LIBADD =			\
+	-lgstpbutils-0.10				\
 	$(RHYTHMBOX_LIBS)
 
 librbbackendsgstreamer_la_LDFLAGS = -export-dynamic
diff --git a/backends/gstreamer/rb-player-gst-xfade.c b/backends/gstreamer/rb-player-gst-xfade.c
index 0dd55e9..e3d625a 100644
--- a/backends/gstreamer/rb-player-gst-xfade.c
+++ b/backends/gstreamer/rb-player-gst-xfade.c
@@ -141,6 +141,10 @@
 #include <gst/controller/gstcontroller.h>
 #include <gst/base/gstbasetransform.h>
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+#include <gst/pbutils/pbutils.h>
+#endif /* HAVE_GSTREAMER_0_10_MISSING_PLUGINS */
+
 #include "rb-player.h"
 #include "rb-player-gst-xfade.h"
 #include "rb-debug.h"
@@ -218,6 +222,7 @@ enum
 {
 	CAN_REUSE_STREAM,
 	REUSE_STREAM,
+	MISSING_PLUGINS,
 	LAST_SIGNAL
 };
 
@@ -298,6 +303,10 @@ typedef struct
 	gboolean emitted_error;
 	gulong error_idle_id;
 	GError *error;
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	GSList *missing_plugins;
+	gulong  emit_missing_plugins_id;
+#endif
 } RBXFadeStream;
 
 #define RB_TYPE_XFADE_STREAM 	(rb_xfade_stream_get_type ())
@@ -636,6 +645,18 @@ rb_player_gst_xfade_class_init (RBPlayerGstXFadeClass *klass)
 			      G_TYPE_NONE,
 			      3,
 			      G_TYPE_STRING, G_TYPE_STRING, GST_TYPE_ELEMENT);
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,	/* no point handling this internally */
+			      NULL, NULL,
+			      rb_marshal_VOID__POINTER_POINTER_POINTER,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_POINTER, G_TYPE_STRV, G_TYPE_STRV);
+#endif
 
 	g_type_class_add_private (klass, sizeof (RBPlayerGstXFadePrivate));
 }
@@ -1362,6 +1383,84 @@ process_tag (const GstTagList *list, const gchar *tag, RBXFadeStream *stream)
 	g_value_unset (&newval);
 }
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+
+static gboolean
+emit_missing_plugins (RBXFadeStream *stream)
+{
+	char **details;
+	char **descriptions;
+	int count;
+	GSList *t;
+	int i;
+
+	stream->emit_missing_plugins_id = 0;
+	count = g_slist_length (stream->missing_plugins);
+
+	details = g_new0 (char *, count + 1);
+	descriptions = g_new0 (char *, count + 1);
+	i = 0;
+	for (t = stream->missing_plugins; t != NULL; t = t->next) {
+		GstMessage *msg = GST_MESSAGE (t->data);
+		char *detail;
+		char *description;
+
+		detail = gst_missing_plugin_message_get_installer_detail (msg);
+		description = gst_missing_plugin_message_get_description (msg);
+		details[i] = g_strdup (detail);
+		descriptions[i] = g_strdup (description);
+		i++;
+
+		gst_message_unref (msg);
+	}
+
+	g_signal_emit (stream->player, signals[MISSING_PLUGINS], 0, stream->stream_data, details, descriptions);
+	g_strfreev (details);
+	g_strfreev (descriptions);
+
+	g_slist_free (stream->missing_plugins);
+	stream->missing_plugins = NULL;
+
+	return FALSE;
+}
+
+
+static void
+rb_player_gst_xfade_handle_missing_plugin_message (RBPlayerGstXFade *player, RBXFadeStream *stream, GstMessage *message)
+{
+	if (stream == NULL) {
+		rb_debug ("got missing-plugin message from unknown stream");
+		return;
+	}
+
+	rb_debug ("got missing-plugin message from %s: %s",
+		  stream->uri,
+		  gst_missing_plugin_message_get_installer_detail (message));
+
+	/* can only handle missing-plugins while prerolling */
+	switch (stream->state) {
+	case PREROLLING:
+	case PREROLL_PLAY:
+		stream->missing_plugins = g_slist_prepend (stream->missing_plugins,
+							   gst_message_ref (message));
+		if (stream->emit_missing_plugins_id == 0) {
+			stream->emit_missing_plugins_id =
+				g_idle_add ((GSourceFunc) emit_missing_plugins,
+					    g_object_ref (stream));
+		}
+
+		/* what do we do now?  if we're missing the decoder
+		 * or something, it'll never preroll..
+		 */
+		break;
+
+	default:
+		rb_debug ("can't process missing-plugin messages for this stream now");
+		break;
+	}
+}
+#endif
+
 /* gstreamer message bus callback */
 static gboolean
 rb_player_gst_xfade_bus_cb (GstBus *bus, GstMessage *message, RBPlayerGstXFade *player)
@@ -1504,25 +1603,29 @@ rb_player_gst_xfade_bus_cb (GstBus *bus, GstMessage *message, RBPlayerGstXFade *
 	}
 	case GST_MESSAGE_ELEMENT:
 	{
-		/* currently only used to report imperfect stream messages */
 		const GstStructure *s;
 		const char *name;
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+		if (gst_is_missing_plugin_message (message)) {
+			rb_player_gst_xfade_handle_missing_plugin_message (player, stream, message);
+			break;
+		}
+#endif
+
 		s = gst_message_get_structure (message);
 		name = gst_structure_get_name (s);
 		if ((strcmp (name, "imperfect-timestamp") == 0) ||
 		    (strcmp (name, "imperfect-offset") == 0)) {
 			char *details;
-			RBXFadeStream *stream;
-			const char *uri = "unknown stream";;
+			const char *uri = "unknown-stream";
 
-			stream = find_stream_by_element (player, GST_ELEMENT (GST_MESSAGE_SRC (message)));
 			if (stream != NULL) {
 				uri = stream->uri;
 			}
 
 			details = gst_structure_to_string (s);
-			rb_debug_real ("check-imperfect", __FILE__, __LINE__, TRUE, "%s: %s", uri, details);
+			rb_debug_real ("check-imperfect", __FILE__, __LINE__, TRUE, "%s: %s", stream->uri, details);
 			g_free (details);
 		}
 		break;
diff --git a/backends/gstreamer/rb-player-gst.c b/backends/gstreamer/rb-player-gst.c
index e737b86..90f3a2a 100644
--- a/backends/gstreamer/rb-player-gst.c
+++ b/backends/gstreamer/rb-player-gst.c
@@ -32,6 +32,10 @@
 #include <libgnomevfs/gnome-vfs-ops.h>
 #include <libgnomevfs/gnome-vfs-utils.h>
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+#include <gst/pbutils/pbutils.h>
+#endif /* HAVE_GSTREAMER_0_10_MISSING_PLUGINS */
+
 #include "rb-debug.h"
 #include "rb-marshal.h"
 #include "rb-util.h"
@@ -90,6 +94,16 @@ enum
 	PROP_PLAYBIN
 };
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+enum
+{
+	MISSING_PLUGINS,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+#endif
+
 struct _RBPlayerGstPrivate
 {
 	char *uri;
@@ -160,6 +174,19 @@ rb_player_gst_class_init (RBPlayerGstClass *klass)
 							      "playbin element",
 							      GST_TYPE_ELEMENT,
 							      G_PARAM_READABLE));
+	
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,	/* no point handling this internally */
+			      NULL, NULL,
+			      rb_marshal_VOID__POINTER_POINTER_POINTER,
+			      G_TYPE_NONE,
+			      3,
+			      G_TYPE_POINTER, G_TYPE_STRV, G_TYPE_STRV);
+#endif
 
 	g_type_class_add_private (klass, sizeof (RBPlayerGstPrivate));
 }
@@ -389,6 +416,42 @@ process_tag (const GstTagList *list, const gchar *tag, RBPlayerGst *player)
 	g_hash_table_insert (player->priv->idle_info_ids, GUINT_TO_POINTER (signal->id), NULL);
 }
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+static void
+rb_player_gst_handle_missing_plugin_message (RBPlayerGst *player, GstMessage *message)
+{
+	char **details;
+	char **descriptions;
+	char *detail;
+	char *description;
+	int count;
+
+	rb_debug ("got missing-plugin message from %s: %s",
+		  GST_OBJECT_NAME (GST_MESSAGE_SRC (message)),
+		  gst_missing_plugin_message_get_installer_detail (message));
+
+	/* probably need to wait to collect any subsequent missing-plugin
+	 * messages, but I think we'd need to wait for state changes to do
+	 * that.  for now, we can only handle a single message.
+	 */
+	count = 1;
+
+	details = g_new0 (char *, count + 1);
+	descriptions = g_new0 (char *, count + 1);
+
+	detail = gst_missing_plugin_message_get_installer_detail (message);
+	description = gst_missing_plugin_message_get_description (message);
+	details[0] = g_strdup (detail);
+	descriptions[0] = g_strdup (description);
+
+	g_signal_emit (player, signals[MISSING_PLUGINS], 0, player->priv->stream_data, details, descriptions);
+	g_strfreev (details);
+	g_strfreev (descriptions);
+
+	gst_message_unref (message);
+}
+#endif
+
 static gboolean
 rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
 {
@@ -504,6 +567,14 @@ rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
 		g_value_set_string (signal->info, gst_structure_get_name (structure));
 		g_idle_add ((GSourceFunc) emit_signal_idle, signal);
 	}
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	case GST_MESSAGE_ELEMENT: {
+		if (gst_is_missing_plugin_message (message)) {
+			rb_player_gst_handle_missing_plugin_message (mp, message);
+		}
+		break;
+	}
+#endif
 	default:
 		break;
 	}
@@ -662,6 +733,7 @@ rb_player_gst_sync_pipeline (RBPlayerGst *mp)
 			return FALSE;
 		}
 	}
+
 	/* FIXME: Set up a timeout to watch if the pipeline doesn't
 	 * go to PAUSED/PLAYING within some time (5 secs maybe?)
 	 */
@@ -694,6 +766,7 @@ end_gstreamer_operation (RBPlayerGst *mp, gboolean op_failed, GError **error)
 			     RB_PLAYER_ERROR_GENERAL,
 			     _("Unknown playback error"));
 	}
+
 }
 
 static void
diff --git a/configure.ac b/configure.ac
index 0cdb99f..35bd609 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,6 +27,7 @@ AC_CHECK_SIZEOF(long)
 DBUS_MIN_REQS=0.35
 GST_0_10_REQS=0.10.4
 GST_0_10_XFADE_REQS=0.10.11
+GST_0_10_MISSING_PLUGIN_REQS=0.10.12
 GTK_REQS=2.8.0
 GNOME_MEDIA_PROFILES_REQS=2.8
 GNOME_VFS_REQS=2.8.0
@@ -273,6 +274,14 @@ if test x"$have_gstreamer_0_10_xfade" = xyes; then
 fi
 AM_CONDITIONAL(USE_GSTREAMER_0_10_XFADE, test x"$have_gstreamer_0_10_xfade" = xyes)
 
+PKG_CHECK_MODULES(GSTREAMER_0_10_MISSING_PLUGINS, \
+		  gstreamer-plugins-base-0.10 >= $GST_0_10_MISSING_PLUGIN_REQS,
+		  have_gstreamer_0_10_missing_plugins=yes,have_gstreamer_0_10_missing_plugins=no)
+if test x"$have_gstreamer_0_10_missing_plugins" = xyes; then
+	AC_DEFINE(HAVE_GSTREAMER_0_10_MISSING_PLUGINS,1,[Define if you want to enable GStreamer plugin installation])
+fi
+AM_CONDITIONAL(USE_GSTREAMER_0_10_MISSING_PLUGINS, test x"$have_gstreamer_0_10_missing_plugins" = xyes)
+
 dnl Tag writing
 AC_ARG_ENABLE(tag-writing,
 	      AC_HELP_STRING([--disable-tag-writing],
diff --git a/lib/rb-marshal.list b/lib/rb-marshal.list
index 61f90d0..2845e25 100644
--- a/lib/rb-marshal.list
+++ b/lib/rb-marshal.list
@@ -1,5 +1,6 @@
 BOOLEAN:BOOLEAN,BOOLEAN,BOOLEAN
 BOOLEAN:POINTER
+BOOLEAN:POINTER,POINTER,POINTER
 BOOLEAN:STRING,STRING,OBJECT
 INT:VOID
 OBJECT:OBJECT
@@ -22,6 +23,7 @@ VOID:POINTER,INT
 VOID:POINTER,INT,POINTER
 VOID:POINTER,LONG,LONG
 VOID:POINTER,POINTER
+VOID:POINTER,POINTER,POINTER
 VOID:POINTER,UINT
 VOID:POINTER,ULONG
 VOID:STRING,DOUBLE
diff --git a/metadata/Makefile.am b/metadata/Makefile.am
index 459d63b..1b5e1c2 100644
--- a/metadata/Makefile.am
+++ b/metadata/Makefile.am
@@ -52,6 +52,7 @@ rhythmbox_metadata_LDADD = 				\
 	librbmetadatasvc.la				\
 	$(top_builddir)/lib/librb.la			\
 	$(RHYTHMBOX_LIBS)				\
+	-lgstpbutils-0.10				\
 	$(DBUS_LIBS)
 
 # test program?
@@ -63,6 +64,7 @@ test_metadata_LDADD =					\
 	librbmetadata.la				\
 	$(top_builddir)/lib/librb.la			\
 	$(RHYTHMBOX_LIBS)				\
+	-lgstpbutils-0.10				\
 	$(DBUS_LIBS)
 
 else
diff --git a/metadata/rb-metadata-dbus-client.c b/metadata/rb-metadata-dbus-client.c
index 018fe2c..9c02259 100644
--- a/metadata/rb-metadata-dbus-client.c
+++ b/metadata/rb-metadata-dbus-client.c
@@ -71,6 +71,8 @@ struct RBMetaDataPrivate
 {
 	char       *uri;
 	char       *mimetype;
+	char      **missing_plugins;
+	char      **plugin_descriptions;
 	GHashTable *metadata;
 };
 
@@ -389,6 +391,35 @@ rb_metadata_load (RBMetaData *md,
 			rb_debug ("couldn't read response message");
 		}
 	}
+	
+	if (*error == NULL) {
+		if (!rb_metadata_dbus_get_strv (&iter, &md->priv->missing_plugins)) {
+			g_set_error (error,
+				     RB_METADATA_ERROR,
+				     RB_METADATA_ERROR_INTERNAL,
+				     _("D-BUS communication error"));
+			rb_debug ("couldn't get missing plugin data from response message");
+		}
+	}
+
+	if (*error == NULL) {
+		if (!rb_metadata_dbus_get_strv (&iter, &md->priv->plugin_descriptions)) {
+			g_set_error (error,
+				     RB_METADATA_ERROR,
+				     RB_METADATA_ERROR_INTERNAL,
+				     _("D-BUS communication error"));
+			rb_debug ("couldn't get missing plugin descriptions from response message");
+		}
+	}
+
+	/* if we're missing some plugins, we'll need to make sure the
+	 * metadata helper rereads the registry before the next load.
+	 * the easiest way to do this is to kill it.
+	 */
+	if (*error == NULL && md->priv->missing_plugins != NULL) {
+		rb_debug ("missing plugins; killing metadata service to force registry reload");
+		kill_metadata_service ();
+	}
 
 	if (*error == NULL) {
 		if (!rb_metadata_dbus_get_boolean (&iter, &ok)) {
@@ -397,23 +428,26 @@ rb_metadata_load (RBMetaData *md,
 				     RB_METADATA_ERROR_INTERNAL,
 				     _("D-BUS communication error"));
 			rb_debug ("couldn't get success flag from response message");
-		} else if (ok) {
-			/* get mime type */
-			if (!rb_metadata_dbus_get_string (&iter, &md->priv->mimetype)) {
-				g_set_error (error,
-					     RB_METADATA_ERROR,
-					     RB_METADATA_ERROR_INTERNAL,
-					     _("D-BUS communication error"));
-			} else {
-				/* get metadata */
-				rb_debug ("got mimetype: %s", md->priv->mimetype);
-				rb_metadata_dbus_read_from_message (md, md->priv->metadata, &iter);
-			}
-		} else {
+		} else if (ok == FALSE) {
 			read_error_from_message (md, &iter, error);
 		}
 	}
 
+	if (ok && *error == NULL) {
+		if (!rb_metadata_dbus_get_string (&iter, &md->priv->mimetype)) {
+			g_set_error (error,
+				     RB_METADATA_ERROR,
+				     RB_METADATA_ERROR_INTERNAL,
+				     _("D-BUS communication error"));
+		} else {
+			rb_debug ("got mimetype: %s", md->priv->mimetype);
+		}
+	}
+
+	if (ok && *error == NULL) {
+		rb_metadata_dbus_read_from_message (md, md->priv->metadata, &iter);
+	}
+
 	if (message)
 		dbus_message_unref (message);
 	if (response)
@@ -431,6 +465,27 @@ rb_metadata_get_mime (RBMetaData *md)
 }
 
 gboolean
+rb_metadata_has_missing_plugins (RBMetaData *md)
+{
+	return (md->priv->missing_plugins != NULL);
+}
+
+gboolean
+rb_metadata_get_missing_plugins (RBMetaData *md,
+				 char ***missing_plugins,
+				 char ***plugin_descriptions)
+{
+	if (md->priv->missing_plugins == NULL) {
+		return FALSE;
+	}
+
+	*missing_plugins = g_strdupv (md->priv->missing_plugins);
+	*plugin_descriptions = g_strdupv (md->priv->plugin_descriptions);
+	return TRUE;
+}
+
+
+gboolean
 rb_metadata_get (RBMetaData *md, RBMetaDataField field,
 		 GValue *ret)
 {
diff --git a/metadata/rb-metadata-dbus-service.c b/metadata/rb-metadata-dbus-service.c
index 5d0115c..53a8cdc 100644
--- a/metadata/rb-metadata-dbus-service.c
+++ b/metadata/rb-metadata-dbus-service.c
@@ -52,14 +52,22 @@ typedef struct {
 	gboolean external;
 } ServiceData;
 
+enum {
+	ERROR_FLAG = 1,
+	MISSING_PLUGINS = 2
+};
+
 static DBusHandlerResult
 _send_error (DBusConnection *connection,
 	     DBusMessage *request,
-	     gboolean include_flag,
+	     int details,
+	     char **missing_plugins,
+	     char **plugin_descriptions,
 	     gint error_type,
 	     const char *message)
 {
 	DBusMessage *reply = dbus_message_new_method_return (request);
+	DBusMessageIter iter;
 
 	if (!message) {
 		message = "";
@@ -68,18 +76,29 @@ _send_error (DBusConnection *connection,
 		rb_debug ("attempting to return error: %s", message);
 	}
 
-	if (include_flag) {
+	dbus_message_iter_init_append (reply, &iter);
+	
+	if (details & MISSING_PLUGINS) {
+		if (!rb_metadata_dbus_add_strv (&iter, missing_plugins)) {
+			rb_debug ("couldn't append missing plugins data");
+			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+		}
+		if (!rb_metadata_dbus_add_strv (&iter, plugin_descriptions)) {
+			rb_debug ("couldn't append missing plugin descriptions");
+			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+		}
+	}
+
+	if (details & ERROR_FLAG) {
 		gboolean ok = FALSE;
-		if (!dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &ok, DBUS_TYPE_INVALID)) {
+		if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &ok)) {
 			rb_debug ("couldn't append error flag");
 			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 		}
 	}
 
-	if (!dbus_message_append_args (reply,
-				       DBUS_TYPE_UINT32, &error_type,
-				       DBUS_TYPE_STRING, &message,
-				       DBUS_TYPE_INVALID)) {
+	if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &error_type) ||
+	    !dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message)) {
 		rb_debug ("couldn't append error data");
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 	}
@@ -100,6 +119,8 @@ rb_metadata_dbus_load (DBusConnection *connection,
 	GError *error = NULL;
 	gboolean ok = TRUE;
 	const char *mimetype = NULL;
+	char **missing_plugins = NULL;
+	char **plugin_descriptions = NULL;
 
 	if (!dbus_message_iter_init (message, &iter)) {
 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -107,21 +128,29 @@ rb_metadata_dbus_load (DBusConnection *connection,
 
 	if (!rb_metadata_dbus_get_string (&iter, &uri)) {
 		/* make translatable? */
-		return _send_error (connection, message, TRUE,
+		return _send_error (connection, message,
+				    ERROR_FLAG | MISSING_PLUGINS,
+				    NULL, NULL,
 				    RB_METADATA_ERROR_INTERNAL,
 				    "Unable to read URI from request");
 	}
 
 	rb_debug ("loading metadata from %s", uri);
-
 	rb_metadata_load (svc->metadata, uri, &error);
 	g_free (uri);
+
+	rb_metadata_get_missing_plugins (svc->metadata, &missing_plugins, &plugin_descriptions);
+
 	if (error != NULL) {
 		DBusHandlerResult r;
 		rb_debug ("metadata error: %s", error->message);
 
-		r = _send_error (connection, message, TRUE, error->code, error->message);
+		r = _send_error (connection, message, 
+				 ERROR_FLAG | MISSING_PLUGINS,
+				 missing_plugins, plugin_descriptions,
+				 error->code, error->message);
 		g_clear_error (&error);
+		g_strfreev (missing_plugins);
 		return r;
 	}
 	rb_debug ("metadata load finished; mimetype = %s", rb_metadata_get_mime (svc->metadata));
@@ -135,6 +164,15 @@ rb_metadata_dbus_load (DBusConnection *connection,
 
 	mimetype = rb_metadata_get_mime (svc->metadata);
 	dbus_message_iter_init_append (reply, &iter);
+	
+	if (!rb_metadata_dbus_add_strv (&iter, missing_plugins)) {
+		rb_debug ("out of memory adding data to return message");
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
+	if (!rb_metadata_dbus_add_strv (&iter, plugin_descriptions)) {
+		rb_debug ("out of memory adding data to return message");
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+	}
 
 	if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &ok) ||
 	    !dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mimetype)) {
@@ -144,7 +182,9 @@ rb_metadata_dbus_load (DBusConnection *connection,
 
 	if (!rb_metadata_dbus_add_to_message (svc->metadata, &iter)) {
 		/* make translatable? */
-		return _send_error (connection, message, TRUE,
+		return _send_error (connection, message,
+				    ERROR_FLAG | MISSING_PLUGINS,
+				    missing_plugins, plugin_descriptions,
 				    RB_METADATA_ERROR_INTERNAL,
 				    "Unable to add metadata to return message");
 	}
@@ -175,7 +215,8 @@ rb_metadata_dbus_can_save (DBusConnection *connection,
 
 	if (!rb_metadata_dbus_get_string (&iter, &mimetype)) {
 		/* make translatable? */
-		return _send_error (connection, message, TRUE,
+		return _send_error (connection, message, ERROR_FLAG,
+				    NULL, NULL,
 				    RB_METADATA_ERROR_INTERNAL,
 				    "Unable to read MIME type from request");
 	}
@@ -233,7 +274,7 @@ rb_metadata_dbus_save (DBusConnection *connection,
 						 data,
 						 &iter)) {
 		/* make translatable? */
-		return _send_error (connection, message, FALSE,
+		return _send_error (connection, message, 0, NULL, NULL,
 				    RB_METADATA_ERROR_INTERNAL,
 				    "Unable to read metadata from message");
 	}
@@ -248,7 +289,7 @@ rb_metadata_dbus_save (DBusConnection *connection,
 		DBusHandlerResult r;
 		rb_debug ("metadata error: %s", error->message);
 
-		r = _send_error (connection, message, FALSE, error->code, error->message);
+		r = _send_error (connection, message, 0, NULL, NULL, error->code, error->message);
 		g_clear_error (&error);
 		return r;
 	}
@@ -385,6 +426,8 @@ test_load (const char *uri)
 	RBMetaData *md;
 	GError *error = NULL;
 	int rv = 0;
+	char **missing_plugins;
+	char **plugin_descriptions;
 
 	md = rb_metadata_new ();
 	rb_metadata_load (md, uri, &error);
@@ -415,6 +458,17 @@ test_load (const char *uri)
 			}
 		}
 	}
+
+	if (rb_metadata_get_missing_plugins (md, &missing_plugins, &plugin_descriptions)) {
+		int i = 0;
+		g_print ("missing plugins:\n");
+		while (missing_plugins[i] != NULL) {
+			g_print ("\t%s (%s)\n", missing_plugins[i], plugin_descriptions[i]);
+			i++;
+		}
+		g_strfreev (missing_plugins);
+	}
+
 	g_object_unref (G_OBJECT (md));
 	return rv;
 }
diff --git a/metadata/rb-metadata-dbus.c b/metadata/rb-metadata-dbus.c
index af62f84..afb57bb 100644
--- a/metadata/rb-metadata-dbus.c
+++ b/metadata/rb-metadata-dbus.c
@@ -64,6 +64,55 @@ rb_metadata_dbus_get_string (DBusMessageIter *iter, gchar **value)
 }
 
 gboolean
+rb_metadata_dbus_get_strv (DBusMessageIter *iter, char ***strv)
+{
+	guint32 count;
+	guint32 i;
+
+	/* strv is stored as a count followed by that many strings */
+	if (rb_metadata_dbus_get_uint32 (iter, &count) == FALSE) {
+		return FALSE;
+	}
+
+	if (count == 0) {
+		*strv = NULL;
+		return TRUE;
+	}
+
+	*strv = g_new0 (char *, count+1);
+	for (i = 0; i < count; i++) {
+		if (rb_metadata_dbus_get_string (iter, (*strv)+i) == FALSE) {
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+gboolean
+rb_metadata_dbus_add_strv (DBusMessageIter *iter, char **strv)
+{
+	guint32 count;
+	guint32 i;
+
+	if (strv == NULL) {
+		count = 0;
+	} else {
+		count = g_strv_length ((char **)strv);
+	}
+
+	if (!dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &count)) {
+		return FALSE;
+	}
+
+	for (i=0; i < count; i++) {
+		if (!dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &strv[i])) {
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+gboolean
 rb_metadata_dbus_add_to_message (RBMetaData *md, DBusMessageIter *iter)
 {
 	DBusMessageIter a_iter;
diff --git a/metadata/rb-metadata-dbus.h b/metadata/rb-metadata-dbus.h
index 43e4731..f67f2d3 100644
--- a/metadata/rb-metadata-dbus.h
+++ b/metadata/rb-metadata-dbus.h
@@ -43,6 +43,11 @@ gboolean	rb_metadata_dbus_get_uint32 (DBusMessageIter *iter,
 					     guint32 *value);
 gboolean	rb_metadata_dbus_get_string (DBusMessageIter *iter,
 					     gchar **value);
+gboolean	rb_metadata_dbus_get_strv (DBusMessageIter *iter,
+					   char ***strv);
+
+gboolean	rb_metadata_dbus_add_strv (DBusMessageIter *iter,
+					   char **strv);
 
 gboolean	rb_metadata_dbus_add_to_message (RBMetaData *md,
 						 DBusMessageIter *iter);
diff --git a/metadata/rb-metadata-gst.c b/metadata/rb-metadata-gst.c
index 47c1048..c407e9b 100644
--- a/metadata/rb-metadata-gst.c
+++ b/metadata/rb-metadata-gst.c
@@ -29,6 +29,9 @@
 #include <gst/tag/tag.h>
 #include <gst/gsturi.h>
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+#include <gst/pbutils/pbutils.h>
+#endif /* HAVE_GSTREAMER_0_10_MISSING_PLUGINS */
 
 #include "rb-metadata.h"
 #include "rb-debug.h"
@@ -49,7 +52,8 @@ const char * ignore_mime_types[] = {
 	"image/",
 	"text/",
 	"application/xml",
-	"application/zip"
+	"application/zip",
+	"application/x-executable"
 };
 
 /*
@@ -83,8 +87,10 @@ struct RBMetaDataPrivate
 	char *type;
 	gboolean handoff;
 	gboolean eos;
-	gboolean non_audio;
+	gboolean has_audio;
+	gboolean has_non_audio;
 	gboolean has_video;
+	GSList *missing_plugins;
 	GError *error;
 };
 
@@ -713,7 +719,7 @@ rb_metadata_gst_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean
 	/* we get "ANY" caps for text/plain files etc. */
 	if (gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
 		rb_debug ("decoded pad with no caps or any caps.  this file is boring.");
-		md->priv->non_audio = TRUE;
+		md->priv->has_non_audio = TRUE;
 		cancel = TRUE;
 	} else {
 		GstPad *sink_pad;
@@ -727,16 +733,17 @@ rb_metadata_gst_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean
 
 		if (g_str_has_prefix (mimetype, "audio/x-raw")) {
 			rb_debug ("got decoded audio pad of type %s", mimetype);
+			md->priv->has_audio = TRUE;
 		} else if (g_str_has_prefix (mimetype, "video/")) {
 			rb_debug ("got decoded video pad of type %s", mimetype);
-			md->priv->non_audio = TRUE;
+			md->priv->has_non_audio = TRUE;
 			md->priv->has_video = TRUE;
 		} else {
 			/* assume anything we can get a video or text stream out of is
 			 * something that should be fed to totem rather than rhythmbox.
 			 */
 			rb_debug ("got decoded pad of non-audio type %s", mimetype);
-			md->priv->non_audio = TRUE;
+			md->priv->has_non_audio = TRUE;
 		}
 	}
 
@@ -753,9 +760,6 @@ rb_metadata_gst_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean
 static void
 rb_metadata_gst_unknown_type_cb (GstElement *decodebin, GstPad *pad, GstCaps *caps, RBMetaData *md)
 {
-	/* try to shortcut it a bit */
-	md->priv->non_audio = TRUE;
-
 	if (!gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) {
 		GstStructure *structure;
 		const gchar *mimetype;
@@ -771,6 +775,9 @@ rb_metadata_gst_unknown_type_cb (GstElement *decodebin, GstPad *pad, GstCaps *ca
 		rb_debug ("decodebin emitted unknown type signal");
 	}
 
+	md->priv->has_non_audio = TRUE;
+#ifndef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	/* try to shortcut it a bit */
 	{
 		char *msg;
 
@@ -778,6 +785,7 @@ rb_metadata_gst_unknown_type_cb (GstElement *decodebin, GstPad *pad, GstCaps *ca
 		GST_ELEMENT_ERROR (md->priv->pipeline, STREAM, CODEC_NOT_FOUND, ("%s", msg), (NULL));
 		g_free (msg);
 	}
+#endif
 }
 
 static GstElement *make_pipeline_element (GstElement *pipeline, const char *element, GError **error)
@@ -796,6 +804,17 @@ static GstElement *make_pipeline_element (GstElement *pipeline, const char *elem
 	return elem;
 }
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+static void
+rb_metadata_handle_missing_plugin_message (RBMetaData *md, GstMessage *message)
+{
+	rb_debug ("got missing-plugin message from %s: %s",
+		  GST_OBJECT_NAME (GST_MESSAGE_SRC (message)),
+		  gst_missing_plugin_message_get_installer_detail (message));
+	md->priv->missing_plugins = g_slist_prepend (md->priv->missing_plugins, gst_message_ref (message));
+}
+#endif
+
 static gboolean
 rb_metadata_bus_handler (GstBus *bus, GstMessage *message, RBMetaData *md)
 {
@@ -809,6 +828,11 @@ rb_metadata_bus_handler (GstBus *bus, GstMessage *message, RBMetaData *md)
 	{
 		GError *gerror;
 		gchar *debug;
+		char *src;
+
+		src = gst_element_get_name (GST_MESSAGE_SRC (message));
+		rb_debug ("got error message from %s", src);
+		g_free (src);
 
 		gst_message_parse_error (message, &gerror, &debug);
 		if (gerror->domain == GST_STREAM_ERROR &&
@@ -819,7 +843,7 @@ rb_metadata_bus_handler (GstBus *bus, GstMessage *message, RBMetaData *md)
 			   md->priv->type != NULL &&
 			   strcmp (md->priv->type, "text/plain") == 0) {
 			rb_debug ("got WRONG_TYPE error for text/plain: setting non-audio flag");
-			md->priv->non_audio = TRUE;
+			md->priv->has_non_audio = TRUE;
 		} else if (md->priv->error) {
 			rb_debug ("caught error: %s, but we've already got one", gerror->message);
 		} else {
@@ -856,10 +880,26 @@ rb_metadata_bus_handler (GstBus *bus, GstMessage *message, RBMetaData *md)
 		}
 		break;
 	}
+	case GST_MESSAGE_ELEMENT:
+	{
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+		if (gst_is_missing_plugin_message (message)) {
+			rb_metadata_handle_missing_plugin_message (md, message);
+		}
+#endif
+		break;
+	}
 	default:
-		rb_debug ("message of type %d", GST_MESSAGE_TYPE (message));
+	{
+		char *src;
+
+		src = gst_element_get_name (GST_MESSAGE_SRC (message));
+		rb_debug ("message of type %s from %s",
+			  GST_MESSAGE_TYPE_NAME (message), src);
+		g_free (src);
 		break;
 	}
+	}
 
 	return FALSE;
 }
@@ -914,8 +954,10 @@ rb_metadata_load (RBMetaData *md,
 	md->priv->error = NULL;
 	md->priv->eos = FALSE;
 	md->priv->handoff = FALSE;
-	md->priv->non_audio = FALSE;
+	md->priv->has_audio = FALSE;
+	md->priv->has_non_audio = FALSE;
 	md->priv->has_video = FALSE;
+	md->priv->missing_plugins = NULL;
 
 	if (md->priv->pipeline) {
 		gst_object_unref (GST_OBJECT (md->priv->pipeline));
@@ -988,7 +1030,6 @@ rb_metadata_load (RBMetaData *md,
 	change_timeout = 0;
 	while (state_ret == GST_STATE_CHANGE_ASYNC &&
 	       !md->priv->eos &&
-	       !md->priv->non_audio &&
 	       change_timeout < 5) {
 		GstMessage *msg;
 
@@ -1009,7 +1050,7 @@ rb_metadata_load (RBMetaData *md,
 
 	if (state_ret != GST_STATE_CHANGE_SUCCESS) {
 		rb_debug ("failed to go to PAUSED for %s", uri);
-		if (!md->priv->non_audio && md->priv->error == NULL)
+		if (!md->priv->has_non_audio && md->priv->error == NULL)
 			g_set_error (error,
 				     RB_METADATA_ERROR,
 				     RB_METADATA_ERROR_INTERNAL,
@@ -1061,7 +1102,8 @@ rb_metadata_load (RBMetaData *md,
 	 * these don't include the URI as the import errors source
 	 * already displays it.
 	 */
-	if (md->priv->non_audio || !md->priv->handoff) {
+	if ((md->priv->has_video || !md->priv->has_audio) &&
+	    (md->priv->has_non_audio || !md->priv->handoff)) {
 		gboolean ignore = FALSE;
 		int i;
 
@@ -1375,3 +1417,59 @@ rb_metadata_set (RBMetaData *md, RBMetaDataField field,
 			     newval);
 	return TRUE;
 }
+
+gboolean
+rb_metadata_has_missing_plugins (RBMetaData *md)
+{
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	return (g_slist_length (md->priv->missing_plugins) > 0);
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+rb_metadata_get_missing_plugins (RBMetaData *md,
+				 char ***missing_plugins,
+				 char ***plugin_descriptions)
+{
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	char **mp;
+	char **pd;
+	int count;
+	int i;
+	GSList *t;
+
+	count = g_slist_length (md->priv->missing_plugins);
+	if (count == 0) {
+		return FALSE;
+	}
+
+	mp = g_new0 (char *, count + 1);
+	pd = g_new0 (char *, count + 1);
+	i = 0;
+	for (t = md->priv->missing_plugins; t != NULL; t = t->next) {
+		GstMessage *msg = GST_MESSAGE (t->data);
+		char *detail;
+		char *description;
+
+		detail = gst_missing_plugin_message_get_installer_detail (msg);
+		description = gst_missing_plugin_message_get_description (msg);
+		rb_debug ("adding [%s,%s] to return data", detail, description);
+		mp[i] = g_strdup (detail);
+		pd[i] = g_strdup (description);
+		i++;
+
+		gst_message_unref (msg);
+	}
+	g_slist_free (md->priv->missing_plugins);
+	md->priv->missing_plugins = NULL;
+
+	*missing_plugins = mp;
+	*plugin_descriptions = pd;
+	return TRUE;
+#else
+	return FALSE;
+#endif
+}
+
diff --git a/metadata/rb-metadata.h b/metadata/rb-metadata.h
index 2810b8f..4c325e1 100644
--- a/metadata/rb-metadata.h
+++ b/metadata/rb-metadata.h
@@ -122,6 +122,12 @@ void		rb_metadata_save	(RBMetaData *md,
 
 const char *	rb_metadata_get_mime	(RBMetaData *md);
 
+gboolean	rb_metadata_has_missing_plugins (RBMetaData *md);
+
+gboolean	rb_metadata_get_missing_plugins (RBMetaData *md,
+					 char ***missing_plugins,
+					 char ***plugin_descriptions);
+
 gboolean	rb_metadata_get		(RBMetaData *md, RBMetaDataField field,
 					 GValue *val);
 
diff --git a/metadata/test-metadata.c b/metadata/test-metadata.c
index 878a46a..d6adb29 100644
--- a/metadata/test-metadata.c
+++ b/metadata/test-metadata.c
@@ -75,6 +75,8 @@ static gboolean
 load_metadata_cb (gpointer file)
 {
 	char *uri = (char *)file;
+	char **missing_plugins;
+	char **plugin_descriptions;
 	GError *error = NULL;
 
 	if (strncmp (uri, "file://", 7)) {
@@ -112,6 +114,15 @@ load_metadata_cb (gpointer file)
 		for (f =(RBMetaDataField)0; f < RB_METADATA_FIELD_LAST; f++)
 			print_metadata_string (md, f, rb_metadata_get_field_name (f));
 	}
+	if (rb_metadata_get_missing_plugins (md, &missing_plugins, &plugin_descriptions)) {
+		int i = 0;
+		g_print ("missing plugins:\n");
+		while (missing_plugins[i] != NULL) {
+			g_print ("\t%s (%s)\n", missing_plugins[i], plugin_descriptions[i]);
+			i++;
+		}
+		g_strfreev (missing_plugins);
+	}
 	printf ("---\n");
 	return FALSE;
 }
diff --git a/podcast/rb-podcast-manager.c b/podcast/rb-podcast-manager.c
index ba9a1e8..135e0c2 100644
--- a/podcast/rb-podcast-manager.c
+++ b/podcast/rb-podcast-manager.c
@@ -70,6 +70,9 @@ enum
 	FINISH_DOWNLOAD,
 	PROCESS_ERROR,
 	FEED_UPDATES_AVAILABLE,
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	MISSING_PLUGINS,
+#endif
 	LAST_SIGNAL
 };
 
@@ -150,7 +153,7 @@ static gboolean rb_podcast_manager_head_query_cb 	(GtkTreeModel *query_model,
 						   	 GtkTreePath *path,
 							 GtkTreeIter *iter,
 						   	 RBPodcastManager *data);
-static gboolean rb_podcast_manager_save_metadata	(RhythmDB *db,
+static void rb_podcast_manager_save_metadata		(RBPodcastManager *pd,
 						  	 RhythmDBEntry *entry,
 						  	 const char *uri);
 static void rb_podcast_manager_db_entry_added_cb 	(RBPodcastManager *pd,
@@ -205,7 +208,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
 	rb_podcast_manager_signals[STATUS_CHANGED] =
 	       g_signal_new ("status_changed",
 		       		G_OBJECT_CLASS_TYPE (object_class),
-		 		GTK_RUN_LAST,
+		 		G_SIGNAL_RUN_LAST,
 				G_STRUCT_OFFSET (RBPodcastManagerClass, status_changed),
 				NULL, NULL,
 				rb_marshal_VOID__BOXED_ULONG,
@@ -217,7 +220,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
 	rb_podcast_manager_signals[START_DOWNLOAD] =
 	       g_signal_new ("start_download",
 		       		G_OBJECT_CLASS_TYPE (object_class),
-		 		GTK_RUN_LAST,
+		 		G_SIGNAL_RUN_LAST,
 				G_STRUCT_OFFSET (RBPodcastManagerClass, start_download),
 				NULL, NULL,
 				g_cclosure_marshal_VOID__BOXED,
@@ -228,7 +231,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
 	rb_podcast_manager_signals[FINISH_DOWNLOAD] =
 	       g_signal_new ("finish_download",
 		       		G_OBJECT_CLASS_TYPE (object_class),
-		 		GTK_RUN_LAST,
+		 		G_SIGNAL_RUN_LAST,
 				G_STRUCT_OFFSET (RBPodcastManagerClass, finish_download),
 				NULL, NULL,
 				g_cclosure_marshal_VOID__BOXED,
@@ -239,7 +242,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
 	rb_podcast_manager_signals[FEED_UPDATES_AVAILABLE] =
 	       g_signal_new ("feed_updates_available",
 		       		G_OBJECT_CLASS_TYPE (object_class),
-		 		GTK_RUN_LAST,
+		 		G_SIGNAL_RUN_LAST,
 				G_STRUCT_OFFSET (RBPodcastManagerClass, feed_updates_available),
 				NULL, NULL,
 				g_cclosure_marshal_VOID__BOXED,
@@ -250,7 +253,7 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
 	rb_podcast_manager_signals[PROCESS_ERROR] =
 	       g_signal_new ("process_error",
 		       		G_OBJECT_CLASS_TYPE (object_class),
-				GTK_RUN_LAST,
+				G_SIGNAL_RUN_LAST,
 				G_STRUCT_OFFSET (RBPodcastManagerClass, process_error),
 				NULL, NULL,
 				g_cclosure_marshal_VOID__STRING,
@@ -258,6 +261,20 @@ rb_podcast_manager_class_init (RBPodcastManagerClass *klass)
 				1,
 				G_TYPE_STRING);
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	rb_podcast_manager_signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,		/* no internal handler */
+			      NULL, NULL,
+			      rb_marshal_BOOLEAN__POINTER_POINTER_POINTER,
+			      G_TYPE_BOOLEAN,
+			      3,
+			      G_TYPE_STRV, G_TYPE_STRV, G_TYPE_CLOSURE);
+#endif
+
+
 	g_type_class_add_private (klass, sizeof (RBPodcastManagerPrivate));
 }
 
@@ -772,7 +789,7 @@ rb_podcast_manager_download_file_info_cb (GnomeVFSAsyncHandle *handle,
 			rhythmdb_entry_set (data->pd->priv->db, data->entry, RHYTHMDB_PROP_MOUNTPOINT, &val);
 			g_value_unset (&val);
 
-			rb_podcast_manager_save_metadata (data->pd->priv->db, data->entry, canon_uri);
+			rb_podcast_manager_save_metadata (data->pd, data->entry, canon_uri);
 
 			g_free (canon_uri);
 
@@ -1021,63 +1038,118 @@ rb_podcast_manager_add_post (RhythmDB *db,
 	return entry;
 }
 
-static gboolean
-rb_podcast_manager_save_metadata (RhythmDB *db, RhythmDBEntry *entry, const char *uri)
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+typedef struct {
+	RhythmDBEntry *entry;
+	RBPodcastManager *mgr;
+} MissingPluginRetryData;
+
+static void
+missing_plugins_retry_cb (gpointer inst, gboolean retry, MissingPluginRetryData *retry_data)
+{
+	const char *uri;
+	if (retry == FALSE)
+		return;
+
+	uri = rhythmdb_entry_get_string (retry_data->entry, RHYTHMDB_PROP_MOUNTPOINT);
+	rb_podcast_manager_save_metadata (retry_data->mgr, retry_data->entry, uri);
+}
+
+static void
+missing_plugins_retry_cleanup (MissingPluginRetryData *retry)
+{
+	g_object_unref (retry->mgr);
+	rhythmdb_entry_unref (retry->entry);
+	g_free (retry);
+}
+
+#endif
+
+static void
+rb_podcast_manager_save_metadata (RBPodcastManager *pd, RhythmDBEntry *entry, const char *uri)
 {
 	RBMetaData *md = rb_metadata_new ();
 	GError *error = NULL;
 	GValue val = { 0, };
 	const char *mime;
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	char **missing_plugins;
+	char **plugin_descriptions;
+#endif
 
-	rb_debug ("Loading podcast metadata from %s", uri);
+	rb_debug ("loading podcast metadata from %s", uri);
         rb_metadata_load (md, uri, &error);
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	if (rb_metadata_get_missing_plugins (md, &missing_plugins, &plugin_descriptions)) {
+		GClosure *closure;
+		gboolean processing;
+		MissingPluginRetryData *data;
+
+		rb_debug ("missing plugins during podcast metadata load for %s", uri);
+		data = g_new0 (MissingPluginRetryData, 1);
+		data->mgr = g_object_ref (pd);
+		data->entry = rhythmdb_entry_ref (entry);
+
+		closure = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
+					  data,
+					  (GClosureNotify) missing_plugins_retry_cleanup);
+		g_closure_set_marshal (closure, g_cclosure_marshal_VOID__BOOLEAN);
+		g_signal_emit (pd, rb_podcast_manager_signals[MISSING_PLUGINS], 0, missing_plugins, plugin_descriptions, closure, &processing);
+		g_closure_sink (closure);
+
+		if (processing) {
+			/* when processing is complete, we'll retry */
+			return;
+		}
+	}
+#endif
+
 	if (error != NULL) {
 		/* this probably isn't an audio enclosure. or some other error */
 		g_value_init (&val, G_TYPE_ULONG);
 		g_value_set_ulong (&val, RHYTHMDB_PODCAST_STATUS_ERROR);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_STATUS, &val);
+		rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_STATUS, &val);
 		g_value_unset (&val);
 
 		g_value_init (&val, G_TYPE_STRING);
 		g_value_set_string (&val, error->message);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &val);
+		rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &val);
 		g_value_unset (&val);
 
-		rhythmdb_commit (db);
+		rhythmdb_commit (pd->priv->db);
 
 		g_object_unref (md);
 		g_error_free (error);
 
-		return FALSE;
+		return;
 	}
 
 	mime = rb_metadata_get_mime (md);
 	if (mime) {
 		g_value_init (&val, G_TYPE_STRING);
 		g_value_set_string (&val, mime);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_MIMETYPE, &val);
+		rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_MIMETYPE, &val);
 		g_value_unset (&val);
 	}
 
 	if (rb_metadata_get (md,
 			     RB_METADATA_FIELD_DURATION,
 			     &val)) {
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_DURATION, &val);
+		rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_DURATION, &val);
 		g_value_unset (&val);
 	}
 
 	if (rb_metadata_get (md,
 			     RB_METADATA_FIELD_BITRATE,
 			     &val)) {
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_BITRATE, &val);
+		rhythmdb_entry_set (pd->priv->db, entry, RHYTHMDB_PROP_BITRATE, &val);
 		g_value_unset (&val);
 	}
 
-	rhythmdb_commit (db);
+	rhythmdb_commit (pd->priv->db);
 
 	g_object_unref (md);
-	return TRUE;
 }
 
 static void
@@ -1265,7 +1337,7 @@ download_progress_cb (GnomeVFSXferProgressInfo *info, gpointer cb_data)
 			rhythmdb_entry_set (data->pd->priv->db, data->entry, RHYTHMDB_PROP_STATUS, &val);
 			g_value_unset (&val);
 
-			rb_podcast_manager_save_metadata (data->pd->priv->db,
+			rb_podcast_manager_save_metadata (data->pd,
 							  data->entry,
 							  canon_uri);
 			g_free (canon_uri);
diff --git a/rhythmdb/rhythmdb-private.h b/rhythmdb/rhythmdb-private.h
index 4a998ee..9971656 100644
--- a/rhythmdb/rhythmdb-private.h
+++ b/rhythmdb/rhythmdb-private.h
@@ -127,6 +127,9 @@ struct RhythmDBPrivate
 	gint read_counter;
 
 	RBMetaData *metadata;
+	gboolean metadata_blocked;
+	GMutex *metadata_lock;
+	GCond *metadata_cond;
 
 	xmlChar **column_xml_names;
 
@@ -138,6 +141,7 @@ struct RhythmDBPrivate
 	GAsyncQueue *action_queue;
 	GAsyncQueue *event_queue;
 	GAsyncQueue *restored_queue;
+	GAsyncQueue *delayed_write_queue;
 	GThreadPool *query_thread_pool;
 
 	GList *stat_list;
diff --git a/rhythmdb/rhythmdb.c b/rhythmdb/rhythmdb.c
index 4f363b8..6b700dd 100644
--- a/rhythmdb/rhythmdb.c
+++ b/rhythmdb/rhythmdb.c
@@ -156,6 +156,7 @@ enum
 	SAVE_COMPLETE,
 	SAVE_ERROR,
 	READ_ONLY,
+	MISSING_PLUGINS,
 	LAST_SIGNAL
 };
 
@@ -317,6 +318,19 @@ rhythmdb_class_init (RhythmDBClass *klass)
 			      1,
 			      G_TYPE_BOOLEAN);
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	rhythmdb_signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,		/* no need for an internal handler */
+			      NULL, NULL,
+			      rb_marshal_BOOLEAN__POINTER_POINTER_POINTER,
+			      G_TYPE_BOOLEAN,
+			      3,
+			      G_TYPE_STRV, G_TYPE_STRV, G_TYPE_CLOSURE);
+#endif
+
 	g_type_class_add_private (klass, sizeof (RhythmDBPrivate));
 }
 
@@ -447,6 +461,7 @@ rhythmdb_init (RhythmDB *db)
 
 	db->priv->action_queue = g_async_queue_new ();
 	db->priv->event_queue = g_async_queue_new ();
+	db->priv->delayed_write_queue = g_async_queue_new ();
 	db->priv->event_queue_watch_id = rb_async_queue_watch_new (db->priv->event_queue,
 								   G_PRIORITY_LOW,		/* really? */
 								   (RBAsyncQueueWatchFunc) rhythmdb_process_one_event,
@@ -461,6 +476,9 @@ rhythmdb_init (RhythmDB *db)
 							 -1, FALSE, NULL);
 
 	db->priv->metadata = rb_metadata_new ();
+	db->priv->metadata_blocked = FALSE;
+	db->priv->metadata_cond = g_cond_new ();
+	db->priv->metadata_lock = g_mutex_new ();
 
 	prop_class = g_type_class_ref (RHYTHMDB_TYPE_PROP_TYPE);
 
@@ -721,6 +739,8 @@ rhythmdb_shutdown (RhythmDB *db)
 	/* FIXME */
 	while ((result = g_async_queue_try_pop (db->priv->event_queue)) != NULL)
 		rhythmdb_event_free (db, result);
+	while ((result = g_async_queue_try_pop (db->priv->delayed_write_queue)) != NULL)
+		rhythmdb_event_free (db, result);
 
 	while ((action = g_async_queue_try_pop (db->priv->action_queue)) != NULL) {
 		rhythmdb_action_free (db, action);
@@ -794,6 +814,7 @@ rhythmdb_finalize (GObject *object)
 	g_async_queue_unref (db->priv->action_queue);
 	g_async_queue_unref (db->priv->event_queue);
 	g_async_queue_unref (db->priv->restored_queue);
+	g_async_queue_unref (db->priv->delayed_write_queue);
 
 	g_mutex_free (db->priv->saving_mutex);
 	g_cond_free (db->priv->saving_condition);
@@ -923,9 +944,21 @@ rhythmdb_read_leave (RhythmDB *db)
 
 	count = g_atomic_int_exchange_and_add (&db->priv->read_counter, -1);
 	rb_debug ("counter: %d", count-1);
-	if (count == 1)
+	if (count == 1) {
+
 		g_signal_emit (G_OBJECT (db), rhythmdb_signals[READ_ONLY],
 			       0, FALSE);
+
+		/* move any delayed writes back to the main event queue */
+		if (g_async_queue_length (db->priv->delayed_write_queue) > 0) {
+			RhythmDBEvent *event;
+			while ((event = g_async_queue_try_pop (db->priv->delayed_write_queue)) != NULL)
+				g_async_queue_push (db->priv->event_queue, event);
+
+			g_main_context_wakeup (g_main_context_default ());
+		}
+
+	}
 }
 
 static gboolean
@@ -1847,8 +1880,7 @@ rhythmdb_add_import_error_entry (RhythmDB *db,
 }
 
 static gboolean
-rhythmdb_process_metadata_load (RhythmDB *db,
-				RhythmDBEvent *event)
+rhythmdb_process_metadata_load_real (RhythmDBEvent *event)
 {
 	RhythmDBEntry *entry;
 	GValue value = {0,};
@@ -1856,7 +1888,7 @@ rhythmdb_process_metadata_load (RhythmDB *db,
 	GTimeVal time;
 
 	if (event->error) {
-		rhythmdb_add_import_error_entry (db, event);
+		rhythmdb_add_import_error_entry (event->db, event);
 		return TRUE;
 	}
 
@@ -1869,14 +1901,14 @@ rhythmdb_process_metadata_load (RhythmDB *db,
 
 	g_get_current_time (&time);
 
-	entry = rhythmdb_entry_lookup_by_location_refstring (db, event->real_uri);
+	entry = rhythmdb_entry_lookup_by_location_refstring (event->db, event->real_uri);
 
 	if (entry != NULL) {
 		if ((event->entry_type != RHYTHMDB_ENTRY_TYPE_INVALID) &&
 		    (rhythmdb_entry_get_entry_type (entry) != event->entry_type)) {
 			/* switching from IGNORE to SONG or vice versa, recreate the entry */
-			rhythmdb_entry_delete (db, entry);
-			rhythmdb_add_timeout_commit (db, FALSE);
+			rhythmdb_entry_delete (event->db, entry);
+			rhythmdb_add_timeout_commit (event->db, FALSE);
 			entry = NULL;
 		}
 	}
@@ -1885,7 +1917,7 @@ rhythmdb_process_metadata_load (RhythmDB *db,
 		if (event->entry_type == RHYTHMDB_ENTRY_TYPE_INVALID)
 			event->entry_type = RHYTHMDB_ENTRY_TYPE_SONG;
 
-		entry = rhythmdb_entry_new (db, event->entry_type, rb_refstring_get (event->real_uri));
+		entry = rhythmdb_entry_new (event->db, event->entry_type, rb_refstring_get (event->real_uri));
 		if (entry == NULL) {
 			rb_debug ("entry already exists");
 			return TRUE;
@@ -1894,20 +1926,20 @@ rhythmdb_process_metadata_load (RhythmDB *db,
 		/* initialize the last played date to 0=never */
 		g_value_init (&value, G_TYPE_ULONG);
 		g_value_set_ulong (&value, 0);
-		rhythmdb_entry_set (db, entry,
+		rhythmdb_entry_set (event->db, entry,
 				    RHYTHMDB_PROP_LAST_PLAYED, &value);
 		g_value_unset (&value);
 
 		/* initialize the rating */
 		g_value_init (&value, G_TYPE_DOUBLE);
 		g_value_set_double (&value, 0);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_RATING, &value);
+		rhythmdb_entry_set (event->db, entry, RHYTHMDB_PROP_RATING, &value);
 		g_value_unset (&value);
 
 	        /* first seen */
 		g_value_init (&value, G_TYPE_ULONG);
 		g_value_set_ulong (&value, time.tv_sec);
-		rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_FIRST_SEEN, &value);
+		rhythmdb_entry_set (event->db, entry, RHYTHMDB_PROP_FIRST_SEEN, &value);
 		g_value_unset (&value);
 	}
 
@@ -1918,36 +1950,119 @@ rhythmdb_process_metadata_load (RhythmDB *db,
 	if (event->vfsinfo) {
 		g_value_init (&value, G_TYPE_ULONG);
 		g_value_set_ulong (&value, event->vfsinfo->mtime);
-		rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_MTIME, &value);
+		rhythmdb_entry_set_internal (event->db, entry, TRUE, RHYTHMDB_PROP_MTIME, &value);
 		g_value_unset (&value);
 	}
 
 	if (event->entry_type != event->ignore_type &&
 	    event->entry_type != event->error_type) {
-		set_props_from_metadata (db, entry, event->vfsinfo, event->metadata);
+		set_props_from_metadata (event->db, entry, event->vfsinfo, event->metadata);
 	}
 
 	/* we've seen this entry */
-	rhythmdb_entry_set_visibility (db, entry, TRUE);
+	rhythmdb_entry_set_visibility (event->db, entry, TRUE);
 
 	g_value_init (&value, G_TYPE_ULONG);
 	g_value_set_ulong (&value, time.tv_sec);
-	rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_LAST_SEEN, &value);
+	rhythmdb_entry_set_internal (event->db, entry, TRUE, RHYTHMDB_PROP_LAST_SEEN, &value);
 	g_value_unset (&value);
 
 	/* Remember the mount point of the volume the song is on */
-	rhythmdb_entry_set_mount_point (db, entry, rb_refstring_get (event->real_uri));
+	rhythmdb_entry_set_mount_point (event->db, entry, rb_refstring_get (event->real_uri));
 
 	/* monitor the file for changes */
 	/* FIXME: watch for errors */
 	if (eel_gconf_get_boolean (CONF_MONITOR_LIBRARY) && event->entry_type == RHYTHMDB_ENTRY_TYPE_SONG)
-		rhythmdb_monitor_uri_path (db, rb_refstring_get (entry->location), NULL);
+		rhythmdb_monitor_uri_path (event->db, rb_refstring_get (entry->location), NULL);
 
-	rhythmdb_add_timeout_commit (db, FALSE);
+	rhythmdb_add_timeout_commit (event->db, FALSE);
 
 	return TRUE;
 }
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+
+static void
+rhythmdb_missing_plugins_cb (gpointer duh, gboolean should_retry, RhythmDBEvent *event)
+{
+	rb_debug ("missing-plugin retry closure called: event %p, retry %d", event, should_retry);
+
+	if (should_retry) {
+		RhythmDBAction *load_action;
+
+		rb_debug ("retrying RHYTHMDB_ACTION_LOAD for %s", rb_refstring_get (event->real_uri));
+		load_action = g_new0 (RhythmDBAction, 1);
+		load_action->type = RHYTHMDB_ACTION_LOAD;
+		load_action->uri = rb_refstring_ref (event->real_uri);
+		load_action->entry_type = RHYTHMDB_ENTRY_TYPE_INVALID;
+		g_async_queue_push (event->db->priv->action_queue, load_action);
+	} else {
+		/* TODO replace event->error with something like
+		 * "Additional GStreamer plugins are required to play this file: %s"
+		 */
+
+		rb_debug ("not retrying RHYTHMDB_ACTION_LOAD for %s", rb_refstring_get (event->real_uri));
+		rhythmdb_process_metadata_load_real (event);
+	}
+}
+
+static void
+rhythmdb_missing_plugin_event_cleanup (RhythmDBEvent *event)
+{
+	rb_debug ("cleaning up missing plugin event %p", event);
+
+	event->db->priv->metadata_blocked = FALSE;
+	g_cond_signal (event->db->priv->metadata_cond);
+
+	g_mutex_unlock (event->db->priv->metadata_lock);
+	rhythmdb_event_free (event->db, event);
+}
+
+#endif /* HAVE_GSTREAMER_0_10_MISSING_PLUGINS */
+
+static gboolean
+rhythmdb_process_metadata_load (RhythmDB *db,
+				RhythmDBEvent *event)
+{
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	char **missing_plugins;
+	char **plugin_descriptions;
+
+	/* don't process missing plugin messages for files we're ignoring */
+	if (g_error_matches (event->error,
+			     RB_METADATA_ERROR,
+			     RB_METADATA_ERROR_NOT_AUDIO_IGNORE)) {
+		return rhythmdb_process_metadata_load_real (event);
+	} else if (rb_metadata_get_missing_plugins (event->metadata,
+					     &missing_plugins,
+					     &plugin_descriptions)) {
+		GClosure *closure;
+		gboolean processing;
+		
+		rb_debug ("missing plugins during metadata load for %s (event = %p)", rb_refstring_get (event->real_uri), event);
+
+		g_mutex_lock (event->db->priv->metadata_lock);
+
+		closure = g_cclosure_new ((GCallback) rhythmdb_missing_plugins_cb,
+					  event,
+					  (GClosureNotify) rhythmdb_missing_plugin_event_cleanup);
+		g_closure_set_marshal (closure, g_cclosure_marshal_VOID__BOOLEAN);
+		g_signal_emit (db, rhythmdb_signals[MISSING_PLUGINS], 0, missing_plugins, plugin_descriptions, closure, &processing);
+		if (processing) {
+			rb_debug ("processing missing plugins");
+		} else {
+			rhythmdb_process_metadata_load_real (event);
+		}
+
+		g_closure_sink (closure);
+		return FALSE;
+	}
+#endif /* HAVE_GSTREAMER_0_10_MISSING_PLUGINS */
+
+	return rhythmdb_process_metadata_load_real (event);
+}
+
+
 static void
 rhythmdb_process_queued_entry_set_event (RhythmDB *db,
 					 RhythmDBEvent *event)
@@ -2008,8 +2123,8 @@ rhythmdb_process_one_event (RhythmDBEvent *event, RhythmDB *db)
 	    ((event->type == RHYTHMDB_EVENT_STAT)
 	     || (event->type == RHYTHMDB_EVENT_METADATA_LOAD)
 	     || (event->type == RHYTHMDB_EVENT_ENTRY_SET))) {
-		rb_debug ("Database is read-only, delaying event processing\n");
-		g_async_queue_push (db->priv->event_queue, event);
+		rb_debug ("Database is read-only, delaying event processing");
+		g_async_queue_push (db->priv->delayed_write_queue, event);
 		return;
 	}
 
@@ -2217,9 +2332,26 @@ rhythmdb_execute_load (RhythmDB *db,
 		event->vfsinfo = NULL;
 	} else {
 		if (event->type == RHYTHMDB_EVENT_METADATA_LOAD) {
+			g_mutex_lock (event->db->priv->metadata_lock);
+			while (event->db->priv->metadata_blocked) {
+				g_cond_wait (event->db->priv->metadata_cond, event->db->priv->metadata_lock);
+			}
+
 			event->metadata = rb_metadata_new ();
 			rb_metadata_load (event->metadata, rb_refstring_get (event->real_uri),
 					  &event->error);
+
+			/* if we're missing some plugins, block further attempts to
+			 * read metadata until we've processed them.
+			 */
+			if (!g_error_matches (event->error,
+					     RB_METADATA_ERROR,
+					     RB_METADATA_ERROR_NOT_AUDIO_IGNORE) &&
+			    rb_metadata_has_missing_plugins (event->metadata)) {
+				event->db->priv->metadata_blocked = TRUE;
+			}
+
+			g_mutex_unlock (event->db->priv->metadata_lock);
 		}
 	}
 
diff --git a/shell/Makefile.am b/shell/Makefile.am
index 1130dce..475e89d 100644
--- a/shell/Makefile.am
+++ b/shell/Makefile.am
@@ -89,7 +89,9 @@ librbshell_la_SOURCES =              			\
 	rb-play-order-random-by-rating.c		\
 	rb-play-order-random-by-rating.h		\
 	rb-tray-icon.c					\
-	rb-tray-icon.h
+	rb-tray-icon.h					\
+	rb-missing-plugins.c				\
+	rb-missing-plugins.h
 
 rhythmbox_LDADD =					\
 	librbshell.la					\
@@ -118,6 +120,10 @@ rhythmbox_LDADD += $(GNOME_MEDIA_PROFILES_LIBS)
 INCLUDES += $(GNOME_MEDIA_PROFILES_CFLAGS)
 endif
 
+if USE_GSTREAMER_0_10_MISSING_PLUGINS
+rhythmbox_LDADD +=					\
+	-lgstpbutils-0.10
+endif
 
 rb-shell-glue.h: rb-shell.xml Makefile
 	$(LIBTOOL) --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell --mode=glib-server --output=$@ $<
diff --git a/shell/rb-missing-plugins.c b/shell/rb-missing-plugins.c
new file mode 100644
index 0000000..118d66b
--- /dev/null
+++ b/shell/rb-missing-plugins.c
@@ -0,0 +1,308 @@
+/* rb-missing-plugins.c
+
+   Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Based on totem-missing-plugins.c, authored by Tim-Philipp Müller <tim centricular net>
+ */
+
+#include "config.h"
+
+#include "rb-missing-plugins.h"
+
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+
+#include "rhythmdb.h"
+#include "rb-shell-player.h"
+#include "rb-debug.h"
+#include "rb-podcast-manager.h"
+
+#include <gst/pbutils/pbutils.h>
+#include <gst/pbutils/install-plugins.h>
+
+#include <gst/gst.h> /* for gst_registry_update */
+
+#include <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include <string.h>
+
+/* list of blacklisted detail strings */
+static GList *blacklisted_plugins = NULL;
+
+typedef struct
+{
+	GClosure   *closure;
+	gchar     **details;
+	RBShell    *shell;
+} RBPluginInstallContext;
+
+static gboolean
+rb_plugin_install_plugin_is_blacklisted (const gchar * detail)
+{
+	GList *res;
+
+	res = g_list_find_custom (blacklisted_plugins,
+	                          detail,
+	                          (GCompareFunc) strcmp);
+
+	return (res != NULL);	
+}
+
+static void
+rb_plugin_install_blacklist_plugin (const gchar * detail)
+{
+	if (!rb_plugin_install_plugin_is_blacklisted (detail)) {
+		blacklisted_plugins = g_list_prepend (blacklisted_plugins,
+		                                      g_strdup (detail));
+	}
+}
+
+static void
+rb_plugin_install_context_free (RBPluginInstallContext *ctx)
+{
+	rb_debug ("cleaning up plugin install context %p", ctx);
+	g_strfreev (ctx->details);
+	g_object_unref (ctx->shell);
+	g_closure_unref (ctx->closure);
+	g_free (ctx);
+}
+
+static void
+rb_plugin_install_done (RBPluginInstallContext *ctx, gboolean retry)
+{
+	GValue param[2] = { {0,}, {0,} };
+
+	rb_debug ("invoking plugin install context %p callback: retry %d", ctx, retry);
+
+	g_value_init (&param[0], G_TYPE_POINTER);
+	g_value_set_pointer (&param[0], NULL);
+	g_value_init (&param[1], G_TYPE_BOOLEAN);
+	g_value_set_boolean (&param[1], retry);
+
+	g_closure_invoke (ctx->closure, NULL, 2, param, NULL);
+	g_value_unset (&param[0]);
+	g_value_unset (&param[1]);
+}
+
+static void
+on_plugin_installation_done (GstInstallPluginsReturn res, gpointer user_data)
+{
+	RBPluginInstallContext *ctx = (RBPluginInstallContext *) user_data;
+	gchar **p;
+	gboolean retry;
+
+	rb_debug ("res = %d (%s)", res, gst_install_plugins_return_get_name (res));
+
+	switch (res)
+	{
+		/* treat partial success the same as success; in the worst case we'll
+		 * just do another round and get NOT_FOUND as result that time */
+		case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS:
+		case GST_INSTALL_PLUGINS_SUCCESS:
+		{
+			/* blacklist installed plugins too, so that we don't get
+			 * into endless installer loops in case of inconsistencies */
+			for (p = ctx->details; p != NULL && *p != NULL; ++p) {
+				rb_plugin_install_blacklist_plugin (*p);
+			}
+
+			g_message ("Missing plugins installed. Updating plugin registry ...");
+
+			/* force GStreamer to re-read its plugin registry */
+			retry = gst_update_registry ();
+
+			rb_plugin_install_done (ctx, retry);
+			break;
+		}
+
+		case GST_INSTALL_PLUGINS_NOT_FOUND:
+		{
+			g_message ("No installation candidate for missing plugins found.");
+
+			/* NOT_FOUND should only be returned if not a single one of the
+			 * requested plugins was found; if we managed to play something
+			 * anyway, we should just continue playing what we have and
+			 * blacklist the requested plugins for this session; if we
+			 * could not play anything we should blacklist them as well,
+			 * so the install wizard isn't called again for nothing */
+			for (p = ctx->details; p != NULL && *p != NULL; ++p) {
+				rb_plugin_install_blacklist_plugin (*p);
+			}
+
+			rb_plugin_install_done (ctx, FALSE);
+			break;
+		}
+
+		case GST_INSTALL_PLUGINS_USER_ABORT:
+		{
+			/* blacklist on user abort, so we show an error next time (or
+			 * just play what we can) instead of calling the installer */
+			for (p = ctx->details; p != NULL && *p != NULL; ++p) {
+				rb_plugin_install_blacklist_plugin (*p);
+			}
+
+			rb_plugin_install_done (ctx, FALSE);
+			break;
+		}
+
+		case GST_INSTALL_PLUGINS_ERROR:
+		case GST_INSTALL_PLUGINS_CRASHED:
+		default:
+		{
+			g_message ("Missing plugin installation failed: %s",
+				   gst_install_plugins_return_get_name (res));
+
+			rb_plugin_install_done (ctx, FALSE);
+			break;
+		}
+	}
+
+	rb_plugin_install_context_free (ctx);
+}
+
+static gboolean
+missing_plugins_event (RBShell *shell, RBPluginInstallContext *ctx)
+{
+	GstInstallPluginsContext *install_ctx;
+	GstInstallPluginsReturn status;
+	int i, num;
+	GtkWindow *window;
+
+	num = g_strv_length (ctx->details);
+	for (i = 0; i < num; ++i) {
+		if (rb_plugin_install_plugin_is_blacklisted (ctx->details[i])) {
+			g_message ("Missing plugin: %s (ignoring)", ctx->details[i]);
+			g_free (ctx->details[i]);
+			ctx->details[i] = ctx->details[num-1];
+			ctx->details[num-1] = NULL;
+			--num;
+			--i;
+		} else {
+			g_message ("Missing plugin: %s", ctx->details[i]);
+		}
+	}
+
+	if (num == 0) {
+		g_message ("All missing plugins are blacklisted, doing nothing");
+		rb_plugin_install_context_free (ctx);
+		return FALSE;
+	}
+
+	install_ctx = gst_install_plugins_context_new ();
+
+	g_object_get (shell, "window", &window, NULL);
+	if (window != NULL && GTK_WIDGET_REALIZED (window)) {
+#ifdef GDK_WINDOWING_X11
+		gulong xid = 0;
+		xid = GDK_WINDOW_XWINDOW (GTK_WIDGET (window)->window);
+		gst_install_plugins_context_set_xid (install_ctx, xid);
+#endif
+		g_object_unref (window);
+	}
+
+	status = gst_install_plugins_async (ctx->details, install_ctx,
+	                                    on_plugin_installation_done,
+	                                    ctx);
+
+	gst_install_plugins_context_free (install_ctx);
+
+	rb_debug ("gst_install_plugins_async() result = %d", status);
+
+	if (status != GST_INSTALL_PLUGINS_STARTED_OK) {
+		if (status == GST_INSTALL_PLUGINS_HELPER_MISSING) {
+			g_message ("Automatic missing codec installation not supported "
+			           "(helper script missing)");
+		} else {
+			g_warning ("Failed to start codec installation: %s",
+			           gst_install_plugins_return_get_name (status));
+		}
+		rb_plugin_install_context_free (ctx);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+missing_plugins_cb (gpointer instance,
+		    const char **details,
+		    const char **descriptions,
+		    GClosure *closure,
+		    RBShell *shell)
+{
+	RBPluginInstallContext *ctx;
+	guint num;
+
+	num = g_strv_length ((char **)details);
+	g_return_val_if_fail (num > 0, FALSE);
+
+	ctx = g_new0 (RBPluginInstallContext, 1);
+	ctx->closure = g_closure_ref (closure);
+	ctx->details = g_strdupv ((char **)details);
+	ctx->shell = g_object_ref (shell);
+
+	return missing_plugins_event (shell, ctx);
+}
+
+#endif /* HAVE_GSTREAMER_0_10_MISSING_PLUGINS */
+
+void
+rb_missing_plugins_init (RBShell *shell)
+{
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	RhythmDB *db;
+	RBShellPlayer *player;
+	RBSource *podcast_source;
+	RBPodcastManager *podcast_mgr;
+
+	g_object_get (shell,
+		      "db", &db,
+		      "shell-player", &player,
+		      NULL);
+	g_signal_connect (player,
+			  "missing-plugins",
+			  G_CALLBACK (missing_plugins_cb),
+			  shell);
+
+	g_signal_connect (db,
+			  "missing-plugins",
+			  G_CALLBACK (missing_plugins_cb),
+			  shell);
+
+	g_object_unref (db);
+	g_object_unref (player);
+
+	podcast_source = rb_shell_get_source_by_entry_type (shell, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED);
+	g_object_get (podcast_source, "podcast-manager", &podcast_mgr, NULL);
+
+	g_signal_connect (podcast_mgr,
+			  "missing-plugins",
+			  G_CALLBACK (missing_plugins_cb),
+			  shell);
+
+	g_object_unref (podcast_mgr);
+
+	gst_pb_utils_init ();
+
+	GST_INFO ("Set up support for automatic missing plugin installation");
+#endif
+}
+
diff --git a/shell/rb-missing-plugins.h b/shell/rb-missing-plugins.h
new file mode 100644
index 0000000..215185f
--- /dev/null
+++ b/shell/rb-missing-plugins.h
@@ -0,0 +1,34 @@
+/* rb-missing-plugins.h
+
+   Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Tim-Philipp Müller <tim centricular net>
+ */
+
+#ifndef RB_MISSING_PLUGINS_H
+#define RB_MISSING_PLUGINS_H
+
+#include "rb-shell.h"
+
+G_BEGIN_DECLS
+
+void rb_missing_plugins_init (RBShell *shell);
+
+G_END_DECLS
+
+#endif /* RB_MISSING_PLUGINS_H */
diff --git a/shell/rb-shell-player.c b/shell/rb-shell-player.c
index 4b554c4..2d6b12a 100644
--- a/shell/rb-shell-player.c
+++ b/shell/rb-shell-player.c
@@ -129,6 +129,9 @@ static void rb_shell_player_sync_replaygain (RBShellPlayer *player,
                                              RhythmDBEntry *entry);
 static void tick_cb (RBPlayer *player, RhythmDBEntry *entry, long elapsed, long duration, gpointer data);
 static void error_cb (RBPlayer *player, RhythmDBEntry *entry, const GError *err, gpointer data);
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+static void missing_plugins_cb (RBPlayer *player, RhythmDBEntry *entry, const char **details, const char **descriptions, RBShellPlayer *sp);
+#endif
 static void playing_stream_cb (RBPlayer *player, RhythmDBEntry *entry, RBShellPlayer *shell_player);
 static void rb_shell_player_error (RBShellPlayer *player, gboolean async, const GError *err);
 
@@ -277,6 +280,7 @@ enum
 	PLAYING_SONG_CHANGED,
 	PLAYING_URI_CHANGED,
 	PLAYING_SONG_PROPERTY_CHANGED,
+	MISSING_PLUGINS,
 	LAST_SIGNAL
 };
 
@@ -503,6 +507,20 @@ rb_shell_player_class_init (RBShellPlayerClass *klass)
 			      G_TYPE_STRING, G_TYPE_STRING,
 			      G_TYPE_VALUE, G_TYPE_VALUE);
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	rb_shell_player_signals[MISSING_PLUGINS] =
+		g_signal_new ("missing-plugins",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      0,	/* no need for an internal handler */
+			      NULL, NULL,
+			      rb_marshal_BOOLEAN__POINTER_POINTER_POINTER,
+			      G_TYPE_BOOLEAN,
+			      3,
+			      G_TYPE_STRV, G_TYPE_STRV, G_TYPE_CLOSURE);
+#endif
+
+
 	g_type_class_add_private (klass, sizeof (RBShellPlayerPrivate));
 }
 
@@ -835,6 +853,13 @@ rb_shell_player_init (RBShellPlayer *player)
 				 G_CALLBACK (playing_stream_cb),
 				 player, 0);
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+	g_signal_connect_object (player->priv->mmplayer,
+				 "missing-plugins",
+				 G_CALLBACK (missing_plugins_cb),
+				 player, 0);
+#endif
+
 	g_signal_connect (G_OBJECT (gnome_vfs_get_volume_monitor ()),
 			  "volume-pre-unmount",
 			  G_CALLBACK (volume_pre_unmount_cb),
@@ -3180,6 +3205,87 @@ tick_cb (RBPlayer *mmplayer,
 	GDK_THREADS_LEAVE ();
 }
 
+#ifdef HAVE_GSTREAMER_0_10_MISSING_PLUGINS
+
+typedef struct {
+	RhythmDBEntry *entry;
+	RBShellPlayer *player;
+} MissingPluginRetryData;
+
+static void
+missing_plugins_retry_cb (gpointer inst,
+			  gboolean retry,
+			  MissingPluginRetryData *retry_data)
+{
+	GError *error = NULL;
+	if (retry == FALSE) {
+		/* next?  or stop playback? */
+		rb_debug ("not retrying playback; stopping player");
+		rb_player_close (retry_data->player->priv->mmplayer, NULL, NULL);
+		return;
+	}
+
+	rb_debug ("retrying playback");
+	rb_shell_player_set_playing_entry (retry_data->player,
+					   retry_data->entry,
+					   FALSE, FALSE,
+					   &error);
+	if (error != NULL) {
+		rb_shell_player_error (retry_data->player, FALSE, error);
+		g_clear_error (&error);
+	}
+}
+
+static void
+missing_plugins_retry_cleanup (MissingPluginRetryData *retry)
+{
+	retry->player->priv->handling_error = FALSE;
+
+	g_object_unref (retry->player);
+	rhythmdb_entry_unref (retry->entry);
+	g_free (retry);
+}
+
+
+static void
+missing_plugins_cb (RBPlayer *player,
+		    RhythmDBEntry *entry,
+		    const char **details,
+		    const char **descriptions,
+		    RBShellPlayer *sp)
+{
+	gboolean processing;
+	GClosure *retry;
+	MissingPluginRetryData *retry_data;
+
+	retry_data = g_new0 (MissingPluginRetryData, 1);
+	retry_data->player = g_object_ref (sp);
+	retry_data->entry = rhythmdb_entry_ref (entry);
+
+	retry = g_cclosure_new ((GCallback) missing_plugins_retry_cb,
+				retry_data,
+				(GClosureNotify) missing_plugins_retry_cleanup);
+	g_closure_set_marshal (retry, g_cclosure_marshal_VOID__BOOLEAN);
+	g_signal_emit (sp,
+		       rb_shell_player_signals[MISSING_PLUGINS], 0,
+		       details, descriptions, retry,
+		       &processing);
+	if (processing) {
+		/* don't handle any further errors */
+		sp->priv->handling_error = TRUE;
+
+		/* probably specify the URI here.. */
+		rb_debug ("stopping player while processing missing plugins");
+		rb_player_close (retry_data->player->priv->mmplayer, NULL, NULL);
+	} else {
+		rb_debug ("not processing missing plugins; simulating EOS");
+		rb_shell_player_handle_eos (NULL, NULL, retry_data->player);
+	}
+
+	g_closure_sink (retry);
+}
+#endif
+
 gboolean
 rb_shell_player_get_playing_path (RBShellPlayer *shell_player,
 				  const gchar **path,
diff --git a/shell/rb-shell.c b/shell/rb-shell.c
index 247a34a..37797fb 100644
--- a/shell/rb-shell.c
+++ b/shell/rb-shell.c
@@ -84,6 +84,7 @@
 #include "rb-sourcelist-model.h"
 #include "rb-song-info.h"
 #include "rb-marshal.h"
+#include "rb-missing-plugins.h"
 
 #define PLAYING_ENTRY_NOTIFY_TIME 4
 
@@ -1372,6 +1373,8 @@ rb_shell_constructor (GType type,
 
 	rb_plugins_engine_init (shell);
 
+	rb_missing_plugins_init (shell);
+
 	g_idle_add ((GSourceFunc)_scan_idle, shell);
 
 	/* GO GO GO! */