[gupnp-tools] av-cp: Make server device async initable



commit 98a2ed00ba692390b87582f3e5ca19bf6021fec2
Author: Jens Georg <mail jensge org>
Date:   Sat Apr 2 20:11:53 2016 +0200

    av-cp: Make server device async initable
    
    Retrieve sort caps and icon
    
    Signed-off-by: Jens Georg <mail jensge org>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=730747

 src/av-cp/playlist-treeview.c |   50 +++++++--
 src/av-cp/server-device.c     |  232 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 262 insertions(+), 20 deletions(-)
---
diff --git a/src/av-cp/playlist-treeview.c b/src/av-cp/playlist-treeview.c
index ad96ba0..458149c 100644
--- a/src/av-cp/playlist-treeview.c
+++ b/src/av-cp/playlist-treeview.c
@@ -75,6 +75,11 @@ void
 on_didl_menuitem_activate (GtkMenuItem *menuitem,
                            gpointer     user_data);
 
+static void
+on_proxy_ready (GObject *source_object,
+                GAsyncResult *res,
+                gpointer user_data);
+
 typedef struct
 {
   GUPnPServiceProxy *content_dir;
@@ -729,9 +734,7 @@ append_didl_object (GUPnPDIDLLiteObject *object,
 }
 
 static void
-on_device_icon_available (GUPnPDeviceInfo *info,
-                          GParamSpec      *spec,
-                          gpointer         user_data)
+update_device_icon (GUPnPDeviceInfo *info)
 {
         GtkTreeModel *model;
         GtkTreeIter   iter;
@@ -775,6 +778,35 @@ get_content_dir (GUPnPDeviceProxy *proxy)
 }
 
 static void
+on_proxy_ready (GObject *source_object,
+                GAsyncResult *res,
+                gpointer user_data)
+{
+        GError *error = NULL;
+        gboolean result = FALSE;
+
+        result = g_async_initable_init_finish (G_ASYNC_INITABLE (source_object),
+                                               res,
+                                               &error);
+
+        if (result == TRUE) {
+                GUPnPServiceProxy *content_dir = GUPNP_SERVICE_PROXY (user_data);
+                char *sort_order = NULL;
+
+                update_device_icon (GUPNP_DEVICE_INFO (source_object));
+
+                g_object_get (source_object,
+                              "sort-order",
+                              &sort_order,
+                              NULL);
+
+                browse (content_dir, "0", 0, MAX_BROWSE);
+                g_free (sort_order);
+        }
+}
+
+
+static void
 on_didl_object_available (GUPnPDIDLLiteParser *parser,
                           GUPnPDIDLLiteObject *object,
                           gpointer             user_data)
@@ -1039,6 +1071,12 @@ append_media_server (GUPnPDeviceProxy *proxy,
                                  -1);
                 g_free (friendly_name);
 
+                g_async_initable_init_async (G_ASYNC_INITABLE (proxy),
+                                             G_PRIORITY_DEFAULT,
+                                             NULL,
+                                             on_proxy_ready,
+                                             content_dir);
+
                 /* Append the embedded devices */
                 child = gupnp_device_info_list_devices (info);
                 while (child) {
@@ -1049,12 +1087,6 @@ append_media_server (GUPnPDeviceProxy *proxy,
                         child = g_list_delete_link (child, child);
                 }
 
-                g_signal_connect (proxy,
-                                  "notify::icon",
-                                  G_CALLBACK (on_device_icon_available),
-                                  NULL);
-
-                browse (content_dir, "0", 0, MAX_BROWSE);
                 gupnp_service_proxy_add_notify (content_dir,
                                                 "ContainerUpdateIDs",
                                                 G_TYPE_STRING,
diff --git a/src/av-cp/server-device.c b/src/av-cp/server-device.c
index f961054..b14f2e9 100644
--- a/src/av-cp/server-device.c
+++ b/src/av-cp/server-device.c
@@ -18,30 +18,91 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+#include <string.h>
+
 #include "server-device.h"
 #include "icons.h"
 
-G_DEFINE_TYPE (AVCPMediaServer, av_cp_media_server, GUPNP_TYPE_DEVICE_PROXY)
+#define CONTENT_DIR "urn:schemas-upnp-org:service:ContentDirectory"
+
+#define AV_CP_MEDIA_SERVER_ERROR av_cp_media_server_error_quark()
+
+GQuark
+av_cp_media_server_error_quark (void);
+
+typedef enum _AVCPMediaServerError {
+        AV_CP_MEDIA_SERVER_FAILED
+} AVCPMediaServerError;
+
+static void
+av_cp_media_server_async_intable_init (gpointer iface, gpointer iface_data);
+
+static void
+av_cp_media_server_init_async (GAsyncInitable      *initable,
+                               int                  io_priority,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data);
+
+static gboolean
+av_cp_media_server_init_finish (GAsyncInitable *initable,
+                                GAsyncResult   *res,
+                                GError         **error);
+static void
+av_cp_media_server_introspect (AVCPMediaServer *self);
+
+static void
+av_cp_media_server_introspect_finish (AVCPMediaServer *self);
+
+G_DEFINE_TYPE_WITH_CODE (AVCPMediaServer,
+                         av_cp_media_server,
+                         GUPNP_TYPE_DEVICE_PROXY,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                                av_cp_media_server_async_intable_init))
+
+enum _AVCPMediaServerInitState {
+        NONE = 0,
+        INITIALIZED,
+        INIT_FAILED,
+        INITIALIZING
+};
+
+typedef enum _AVCPMediaServerInitState AVCPMediaServerInitState;
 
 struct _AVCPMediaServerPrivate {
         GdkPixbuf *icon;
+        char *default_sort_order;
+        GList *tasks;
+        AVCPMediaServerInitState state;
 };
 
 enum
 {
         PROP_ICON = 1,
+        PROP_SORT_ORDER,
         N_PROPERTIES
 };
 
 static GParamSpec *av_cp_media_server_properties[N_PROPERTIES] = { NULL, };
 
 /* GObject overrides */
+
 static void
-av_cp_media_server_constructed (GObject *obj);
+av_cp_media_server_finalize (GObject *object)
+{
+        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (object);
+        GObjectClass *parent_class =
+                              G_OBJECT_CLASS (av_cp_media_server_parent_class);
+
+        g_clear_pointer (&self->priv->default_sort_order, g_free);
+
+        parent_class->finalize (object);
+}
 
 static void
 av_cp_media_server_dispose (GObject *object)
 {
+        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (object);
         GObjectClass *parent_class =
                               G_OBJECT_CLASS (av_cp_media_server_parent_class);
 
@@ -62,6 +123,9 @@ av_cp_media_server_get_property (GObject    *obj,
         case PROP_ICON:
                 g_value_set_object (value, self->priv->icon);
                 break;
+        case PROP_SORT_ORDER:
+                g_value_set_string (value, self->priv->default_sort_order);
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, spec);
                 break;
@@ -73,13 +137,53 @@ av_cp_media_server_on_icon_updated (GUPnPDeviceInfo *info,
                                     GdkPixbuf       *icon);
 
 static void
+av_cp_media_server_on_get_sort_caps (GUPnPServiceProxy *proxy,
+                                     GUPnPServiceProxyAction *action,
+                                     gpointer user_data)
+{
+        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (user_data);
+        GError *error = NULL;
+        char *sort_caps = NULL;
+
+        gupnp_service_proxy_end_action (proxy,
+                                        action,
+                                        &error,
+                                        "SortCaps",
+                                        G_TYPE_STRING,
+                                        &sort_caps,
+                                        NULL);
+        if (error != NULL) {
+                g_warning ("Failed to get sort caps from server: %s",
+                           error->message);
+                g_error_free (error);
+        } else {
+                GString *default_sort_order = g_string_new (NULL);
+                if (strstr (sort_caps, "upnp:class") != NULL) {
+                        g_string_append (default_sort_order, "+upnp:class,");
+                }
+
+                if (strstr (sort_caps, "dc:title") != NULL) {
+                        g_string_append (default_sort_order, "+dc:title");
+                }
+
+                self->priv->default_sort_order =
+                                g_string_free (default_sort_order, FALSE);
+        }
+
+        self->priv->state = INITIALIZED;
+        av_cp_media_server_introspect_finish (self);
+        g_object_notify (G_OBJECT (self), "sort-order");
+        g_object_unref (self);
+}
+
+static void
 av_cp_media_server_class_init (AVCPMediaServerClass *klass)
 {
         GObjectClass *obj_class = G_OBJECT_CLASS (klass);
 
-        obj_class->constructed = av_cp_media_server_constructed;
         obj_class->get_property = av_cp_media_server_get_property;
         obj_class->dispose = av_cp_media_server_dispose;
+        obj_class->finalize = av_cp_media_server_finalize;
 
         g_type_class_add_private (klass, sizeof (AVCPMediaServerPrivate));
 
@@ -91,6 +195,14 @@ av_cp_media_server_class_init (AVCPMediaServerClass *klass)
                                      G_PARAM_STATIC_STRINGS |
                                      G_PARAM_READABLE);
 
+        av_cp_media_server_properties[PROP_SORT_ORDER] =
+                g_param_spec_string ("sort-order",
+                                     "sort-order",
+                                     "sort-order",
+                                     NULL,
+                                     G_PARAM_STATIC_STRINGS |
+                                     G_PARAM_READABLE);
+
         g_object_class_install_properties (obj_class,
                                            N_PROPERTIES,
                                            av_cp_media_server_properties);
@@ -105,22 +217,120 @@ av_cp_media_server_init (AVCPMediaServer *self)
 }
 
 static void
-av_cp_media_server_constructed (GObject *obj)
+av_cp_media_server_on_icon_updated (GUPnPDeviceInfo *info,
+                                    GdkPixbuf       *icon)
 {
-        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (obj);
+        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (info);
+        GUPnPServiceInfo *proxy = NULL;
+
+        self->priv->icon = icon;
+        proxy = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (self),
+                                               CONTENT_DIR);
+        if (proxy != NULL) {
+                gupnp_service_proxy_begin_action
+                                (GUPNP_SERVICE_PROXY (proxy),
+                                "GetSortCapabilities",
+                                av_cp_media_server_on_get_sort_caps,
+                                g_object_ref (self),
+                                NULL);
+        } else {
+                g_debug ("Invalid MediaServer device without ContentDirectory");
+                self->priv->state = INIT_FAILED;
+                av_cp_media_server_introspect_finish (self);
+        }
+}
+
+
+static void
+av_cp_media_server_async_intable_init (gpointer iface, gpointer iface_data)
+{
+        GAsyncInitableIface *aii = (GAsyncInitableIface *)iface;
+        aii->init_async = av_cp_media_server_init_async;
+        aii->init_finish = av_cp_media_server_init_finish;
+}
+
+static void
+av_cp_media_server_init_async (GAsyncInitable      *initable,
+                               int                  io_priority,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (initable);
+        GTask *task;
+
+        task = g_task_new (initable, cancellable, callback, user_data);
+        switch (self->priv->state) {
+                case INITIALIZED:
+                        g_task_return_boolean (task, TRUE);
+                        g_object_unref (task);
+
+                        break;
+                case INIT_FAILED:
+                        g_task_return_new_error (task,
+                                                 AV_CP_MEDIA_SERVER_ERROR,
+                                                 AV_CP_MEDIA_SERVER_FAILED,
+                                                 "Initialisation failed");
+                        g_object_unref (task);
+                        break;
+                case NONE:
+                        self->priv->state = INITIALIZING;
+                        self->priv->tasks = g_list_prepend (self->priv->tasks,
+                                                            task);
+                        av_cp_media_server_introspect (self);
+                        break;
+                case INITIALIZING:
+                        self->priv->tasks = g_list_prepend (self->priv->tasks,
+                                                            task);
+                        break;
+                default:
+                        g_assert_not_reached ();
+                        break;
+        }
+}
 
-        G_OBJECT_CLASS (av_cp_media_server_parent_class)->constructed (obj);
+static gboolean
+av_cp_media_server_init_finish (GAsyncInitable *initable,
+                                GAsyncResult   *res,
+                                GError         **error)
+{
+        g_return_val_if_fail (g_task_is_valid (res, initable), FALSE);
+
+        return g_task_propagate_boolean (G_TASK (res), error);
+}
 
+static void
+av_cp_media_server_introspect (AVCPMediaServer *self)
+{
         schedule_icon_update (GUPNP_DEVICE_INFO (self),
                               av_cp_media_server_on_icon_updated);
 }
 
 static void
-av_cp_media_server_on_icon_updated (GUPnPDeviceInfo *info,
-                                    GdkPixbuf       *icon)
+av_cp_media_server_introspect_finish (AVCPMediaServer *self)
 {
-        AVCPMediaServer *self = AV_CP_MEDIA_SERVER (info);
+        GList *l;
 
-        self->priv->icon = icon;
-        g_object_notify (G_OBJECT (info), "icon");
+        for (l = self->priv->tasks; l != NULL; l = l->next) {
+                GTask *task = l->data;
+
+                if (self->priv->state == INITIALIZED) {
+                        g_task_return_boolean (task, TRUE);
+                } else {
+                        g_task_return_new_error (task,
+                                                 AV_CP_MEDIA_SERVER_ERROR,
+                                                 AV_CP_MEDIA_SERVER_FAILED,
+                                                 "Initialisation failed");
+                }
+
+                g_object_unref (task);
+        }
+
+        g_clear_pointer (&self->priv->tasks, g_list_free);
+}
+
+GQuark
+av_cp_media_server_error_quark (void)
+{
+        return g_quark_from_static_string ("av-cp-media-server-error-quark");
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]