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 (¶m[0], G_TYPE_POINTER);
+ g_value_set_pointer (¶m[0], NULL);
+ g_value_init (¶m[1], G_TYPE_BOOLEAN);
+ g_value_set_boolean (¶m[1], retry);
+
+ g_closure_invoke (ctx->closure, NULL, 2, param, NULL);
+ g_value_unset (¶m[0]);
+ g_value_unset (¶m[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! */