summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Georg <mail@jensge.org>2016-04-02 20:11:53 +0200
committerJens Georg <mail@jensge.org>2016-04-25 23:20:53 +0200
commit98a2ed00ba692390b87582f3e5ca19bf6021fec2 (patch)
treebbf1d04c0bec2832d07aa0f33632564d74292075
parentf18b0dc1f40a80d03f56d8a290a49a8c2e161b4b (diff)
downloadgupnp-tools-98a2ed00ba692390b87582f3e5ca19bf6021fec2.tar.gz
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
-rw-r--r--src/av-cp/playlist-treeview.c50
-rw-r--r--src/av-cp/server-device.c232
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");
}