[gupnp-tools/wip/search: 1/2] av-cp: Implement sliced search



commit f46e85fdac295f24f78e4461a839d7dc30eb323f
Author: Jens Georg <mail jensge org>
Date:   Thu Aug 18 12:50:03 2016 +0200

    av-cp: Implement sliced search
    
    Signed-off-by: Jens Georg <mail jensge org>

 src/av-cp/search-dialog.c |  303 ++++++++++++++++++++++++++++++++++++---------
 1 files changed, 243 insertions(+), 60 deletions(-)
---
diff --git a/src/av-cp/search-dialog.c b/src/av-cp/search-dialog.c
index 8bec57c..018eeea 100644
--- a/src/av-cp/search-dialog.c
+++ b/src/av-cp/search-dialog.c
@@ -27,6 +27,8 @@
 #include "server-device.h"
 #include "icons.h"
 
+typedef struct _SearchTask SearchTask;
+
 struct _SearchDialog {
         GtkDialog parent;
 };
@@ -42,6 +44,7 @@ struct _SearchDialogPrivate {
         char *title;
         AVCPMediaServer *server;
         guint pulse_timer;
+        SearchTask *task;
 };
 
 typedef struct _SearchDialogPrivate SearchDialogPrivate;
@@ -53,6 +56,192 @@ search_dialog_on_search_activate (SearchDialog *self, GtkEntry *entry);
 static void
 search_dialog_finalize (GObject *object);
 
+struct _SearchTask {
+        AVCPMediaServer *server;
+        char *search_expression;
+        char *container_id;
+        GtkListStore *target;
+        int start;
+        int count;
+        int total;
+        gboolean running;
+        GUPnPDIDLLiteParser *parser;
+        GError *error;
+        GSourceFunc callback;
+        gpointer user_data;
+};
+
+static void
+search_task_on_didl_object_available (GUPnPDIDLLiteParser *parser,
+                                      GUPnPDIDLLiteObject *object,
+                                      gpointer             user_data);
+
+static SearchTask *
+search_task_new (AVCPMediaServer *server,
+                 GtkListStore *target,
+                 const char *container_id,
+                 const char *search_expression,
+                 int count,
+                 GSourceFunc callback,
+                 gpointer user_data)
+{
+        SearchTask *task = g_new0 (SearchTask, 1);
+
+        task->search_expression = g_strdup (search_expression);
+        task->target = g_object_ref (target);
+        task->server = g_object_ref (server);
+        task->container_id = g_strdup (container_id);
+        task->start = 0;
+        task->count = count;
+        task->total = -1;
+        task->parser = gupnp_didl_lite_parser_new ();
+        task->error = NULL;
+        task->callback = callback;
+        task->user_data = user_data;
+
+        g_signal_connect (G_OBJECT (task->parser),
+                          "object-available",
+                          G_CALLBACK (search_task_on_didl_object_available),
+                          task);
+
+        return task;
+}
+
+static void
+search_task_free (SearchTask *task) {
+        g_clear_object (&task->target);
+        g_clear_object (&task->server);
+        g_clear_object (&task->parser);
+        g_free (task->search_expression);
+        g_free (task->container_id);
+        if (task->error != NULL) {
+                g_error_free (task->error);
+        }
+        g_free (task);
+}
+
+static gboolean
+search_task_idle_callback (gpointer user_data)
+{
+        SearchTask *task = (SearchTask *)user_data;
+        if (task->callback != NULL) {
+            task->callback (task->user_data);
+        }
+
+        return FALSE;
+}
+
+static void
+search_task_set_finished (SearchTask *task, GError *error)
+{
+        task->running = FALSE;
+        task->error = error;
+
+        g_idle_add (search_task_idle_callback, task);
+}
+
+static void
+search_task_on_search_ready (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+        SearchTask *task  = (SearchTask *)user_data;
+        GError *error = NULL;
+        char *didl_xml = NULL;
+        guint32 total = 0;
+        guint32 returned = 0;
+        gboolean result;
+        gboolean finished = FALSE;
+
+        result = av_cp_media_server_search_finish (AV_CP_MEDIA_SERVER (source),
+                                                   res,
+                                                   &didl_xml,
+                                                   &total,
+                                                   &returned,
+                                                   &error);
+
+        g_message ("Received search slice result for %s with expression %s, result is %s",
+                   task->container_id,
+                   task->search_expression,
+                   result ? "TRUE" : "FALSE");
+
+
+        if (!result) {
+                finished = TRUE;
+
+                goto out;
+        }
+
+        /* Nothing returned by the server */
+        if (returned == 0) {
+                finished = TRUE;
+
+                goto out;
+        }
+
+        if (didl_xml == NULL) {
+                finished = TRUE;
+                goto out;
+        }
+
+        gupnp_didl_lite_parser_parse_didl (task->parser, didl_xml, &error);
+        if (error != NULL) {
+                finished = TRUE;
+
+                goto out;
+        }
+
+        if (total != 0) {
+                task->total = total;
+        }
+
+        task->start += returned;
+
+out:
+        g_clear_pointer (&didl_xml, g_free);
+        if (finished) {
+            g_message ("Finished search, error: %s",
+                       error ? error->message : "none");
+                search_task_set_finished (task, error);
+        } else {
+            g_message ("Starting new slice %u/%u (total %u)",
+                       task->start,
+                       task->count,
+                       task->total);
+
+            av_cp_media_server_search_async (task->server,
+                                             NULL,
+                                             search_task_on_search_ready,
+                                             task->container_id,
+                                             task->search_expression,
+                                             task->start,
+                                             task->count,
+                                             task);
+        }
+}
+
+static void
+search_task_run (SearchTask *task) {
+        if (task->running) {
+                g_debug ("Search task is already running, not doing anything.");
+
+                return;
+        }
+
+        g_message ("Starting search task for %s with expression %s",
+                   task->container_id,
+                   task->search_expression);
+
+        task->running = TRUE;
+
+        av_cp_media_server_search_async (task->server,
+                                         NULL,
+                                         search_task_on_search_ready,
+                                         task->container_id,
+                                         task->search_expression,
+                                         task->start,
+                                         task->count,
+                                         task);
+}
+
 #define ITEM_CLASS_IMAGE "object.item.imageItem"
 #define ITEM_CLASS_AUDIO "object.item.audioItem"
 #define ITEM_CLASS_VIDEO "object.item.videoItem"
@@ -87,6 +276,21 @@ get_item_icon (GUPnPDIDLLiteObject *object)
         return icon;
 }
 
+static void
+search_task_on_didl_object_available (GUPnPDIDLLiteParser *parser,
+                                      GUPnPDIDLLiteObject *object,
+                                      gpointer             user_data)
+{
+        SearchTask *task = (SearchTask *)user_data;
+        GtkTreeIter iter;
+
+        gtk_list_store_insert_with_values (task->target,
+                                           &iter,
+                                           -1,
+                                           0, get_item_icon (object),
+                                           1, gupnp_didl_lite_object_get_title (object),
+                                           -1);
+}
 
 static void
 search_dialog_class_init (SearchDialogClass *klass)
@@ -142,72 +346,39 @@ search_dialog_finalize (GObject *object)
         }
 }
 
-static void
-on_didl_object_available (GUPnPDIDLLiteParser *parser,
-                          GUPnPDIDLLiteObject *object,
-                          gpointer             user_data)
-{
-        SearchDialog *self = SEARCH_DIALOG (user_data);
-        SearchDialogPrivate *priv = search_dialog_get_instance_private (self);
-        GtkTreeIter iter;
-
-        gtk_list_store_insert_with_values (priv->search_dialog_liststore,
-                                           &iter,
-                                           -1,
-                                           0, get_item_icon (object),
-                                           1, gupnp_didl_lite_object_get_title (object),
-                                           -1);
-
-}
-
-static void
-search_dialog_on_search_done (GObject *source, GAsyncResult *res, gpointer user_data)
+static gboolean
+search_dialog_on_search_task_done (gpointer user_data)
 {
         SearchDialog *self = SEARCH_DIALOG (user_data);
         SearchDialogPrivate *priv = search_dialog_get_instance_private (self);
 
-        GError *error = NULL;
-        char *xml = NULL;
-        guint32 total = 0;
-        guint32 returned = 0;
-        GUPnPDIDLLiteParser *parser;
-
+        g_source_remove (priv->pulse_timer);
+        gtk_entry_set_progress_fraction (priv->search_dialog_entry, 0);
+        gtk_widget_set_sensitive (GTK_WIDGET (priv->search_dialog_entry), TRUE);
 
-        if (!av_cp_media_server_search_finish (AV_CP_MEDIA_SERVER (source),
-                                               res,
-                                               &xml,
-                                               &total,
-                                               &returned,
-                                               &error)) {
+        if (priv->task->error != NULL) {
                 GtkWidget *dialog = NULL;
 
                 dialog = gtk_message_dialog_new (GTK_WINDOW (self),
                                                  GTK_DIALOG_DESTROY_WITH_PARENT,
                                                  GTK_MESSAGE_WARNING,
                                                  GTK_BUTTONS_CLOSE,
-                                                 "Search failed: %s",
-                                                 error->message);
+                                                 "%s",
+                                                 _("Search failed"));
+
+                gtk_message_dialog_format_secondary_text
+                                    (GTK_MESSAGE_DIALOG (dialog),
+                                     _("Error message was: %s"),
+                                     priv->task->error->message);
                 gtk_dialog_run (GTK_DIALOG (dialog));
                 gtk_widget_destroy (dialog);
 
-                g_critical ("Failed to search: %s", error->message),
-                g_error_free (error);
-
-                goto out;
+                g_critical ("Failed to search: %s", priv->task->error->message);
         }
 
-        parser = gupnp_didl_lite_parser_new ();
-        g_signal_connect (G_OBJECT (parser), "object-available",
-                G_CALLBACK (on_didl_object_available),
-                self);
-        gupnp_didl_lite_parser_parse_didl (parser, xml, &error);
-        g_free (xml);
+        g_clear_pointer (&priv->task, search_task_free);
 
-out:
-        g_clear_object (&parser);
-        g_source_remove (priv->pulse_timer);
-        gtk_entry_set_progress_fraction (priv->search_dialog_entry, 0.0);
-        gtk_widget_set_sensitive (GTK_WIDGET (priv->search_dialog_entry), TRUE);
+        return FALSE;
 }
 
 void
@@ -258,7 +429,17 @@ search_dialog_set_container_title (SearchDialog *self, char *title)
 static gboolean
 pulse_timer (gpointer user_data)
 {
-        gtk_entry_progress_pulse (GTK_ENTRY (user_data));
+        SearchDialog *self = SEARCH_DIALOG (user_data);
+        SearchDialogPrivate *priv = search_dialog_get_instance_private (self);
+        if (priv->task->total == -1) {
+                gtk_entry_progress_pulse (GTK_ENTRY (priv->search_dialog_entry));
+        } else {
+                gdouble progress = (gdouble) priv->task->start /
+                                   (gdouble) priv->task->total;
+                gtk_entry_set_progress_fraction
+                                    (GTK_ENTRY (priv->search_dialog_entry),
+                                     progress);
+        }
 
         return TRUE;
 }
@@ -270,15 +451,17 @@ search_dialog_on_search_activate (SearchDialog *self, GtkEntry *entry)
         SearchDialogPrivate *priv = search_dialog_get_instance_private (self);
         gtk_list_store_clear (priv->search_dialog_liststore);
         gtk_widget_set_sensitive (GTK_WIDGET (entry), FALSE);
-        priv->pulse_timer = g_timeout_add_seconds (1, pulse_timer, entry);
-
-        av_cp_media_server_search_async (priv->server,
-                                         NULL,
-                                         search_dialog_on_search_done,
-                                         priv->id,
-                                         gtk_entry_get_text (entry),
-                                         0,
-                                         0,
-                                         self);
+        priv->pulse_timer = g_timeout_add_seconds (1, pulse_timer, self);
+
+        g_clear_pointer (&priv->task, search_task_free);
+
+        priv->task = search_task_new (priv->server,
+                                      priv->search_dialog_liststore,
+                                      priv->id,
+                                      gtk_entry_get_text (entry),
+                                      30,
+                                      search_dialog_on_search_task_done,
+                                      self);
+        search_task_run (priv->task);
 }
 


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