[PATCH 2/5] tracker: add notification support



From: Lionel Landwerlin <lionel g landwerlin linux intel com>

Signed-off-by: Lionel Landwerlin <lionel g landwerlin linux intel com>
---
 src/tracker/Makefile.am         |    4 +-
 src/tracker/grl-tracker-utils.c |   73 ++++++
 src/tracker/grl-tracker-utils.h |   19 ++
 src/tracker/grl-tracker.c       |  530 +++++++++++++++++++++++++++++++--------
 4 files changed, 516 insertions(+), 110 deletions(-)
 create mode 100644 src/tracker/grl-tracker-utils.c
 create mode 100644 src/tracker/grl-tracker-utils.h

diff --git a/src/tracker/Makefile.am b/src/tracker/Makefile.am
index 5510f5a..7e407cb 100644
--- a/src/tracker/Makefile.am
+++ b/src/tracker/Makefile.am
@@ -21,7 +21,9 @@ libgrltracker_la_LDFLAGS = \
 
 libgrltracker_la_SOURCES = 	\
 	grl-tracker.c 		\
-	grl-tracker.h
+	grl-tracker.h		\
+	grl-tracker-utils.c	\
+	grl-tracker-utils.h
 
 libdir = $(GRL_PLUGINS_DIR)
 trackerxmldir = $(GRL_PLUGINS_CONF_DIR)
diff --git a/src/tracker/grl-tracker-utils.c b/src/tracker/grl-tracker-utils.c
new file mode 100644
index 0000000..79829dd
--- /dev/null
+++ b/src/tracker/grl-tracker-utils.c
@@ -0,0 +1,73 @@
+#include <glib.h>
+
+#include "grl-tracker-utils.h"
+
+/* TODO: is it worth to have an LRU ? */
+struct _GrlTrackerItemCache {
+  gsize size_limit;
+  gsize size_current;
+
+  GHashTable *table;
+  GList *list;
+};
+
+GrlTrackerItemCache *
+grl_tracker_item_cache_new (gsize size)
+{
+  GrlTrackerItemCache *cache;
+
+  g_return_val_if_fail (size > 0, NULL);
+
+  cache = g_slice_new0 (GrlTrackerItemCache);
+
+  if (!cache)
+    return NULL;
+
+  cache->size_limit = size;
+  cache->table = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+  return cache;
+}
+
+void
+grl_tracker_item_cache_free (GrlTrackerItemCache *cache)
+{
+  g_return_if_fail (cache != NULL);
+
+  g_list_free (cache->list);
+  g_hash_table_destroy (cache->table);
+
+  g_slice_free (GrlTrackerItemCache, cache);
+}
+
+void
+grl_tracker_item_cache_add_item (GrlTrackerItemCache *cache,
+				 guint id,
+				 GrlTrackerSource *source)
+{
+  GList *last;
+
+  g_return_if_fail (cache != NULL);
+
+  if (g_hash_table_lookup (cache->table, GSIZE_TO_POINTER (id)) != NULL)
+    return;
+
+  if (cache->size_current >= cache->size_limit) {
+    last = g_list_last (cache->list); /* TODO: optimize that ! */
+    g_hash_table_remove (cache->table, last->data);
+    cache->list = g_list_remove_link (cache->list, last);
+  }
+
+  cache->list = g_list_prepend (cache->list, GSIZE_TO_POINTER (id));
+  g_hash_table_insert (cache->table, GSIZE_TO_POINTER (id), source);
+  cache->size_current++;
+}
+
+GrlTrackerSource *
+grl_tracker_item_cache_get_source (GrlTrackerItemCache *cache, guint id)
+{
+  g_return_val_if_fail (cache != NULL, NULL);
+
+  return (GrlTrackerSource *) g_hash_table_lookup (cache->table,
+						   GSIZE_TO_POINTER (id));
+}
diff --git a/src/tracker/grl-tracker-utils.h b/src/tracker/grl-tracker-utils.h
new file mode 100644
index 0000000..be82e0d
--- /dev/null
+++ b/src/tracker/grl-tracker-utils.h
@@ -0,0 +1,19 @@
+#ifndef _GRL_TRACKER_UTILS_H_
+#define _GRL_TRACKER_UTILS_H_
+
+#include "grl-tracker.h"
+
+typedef struct _GrlTrackerItemCache GrlTrackerItemCache;
+
+GrlTrackerItemCache *grl_tracker_item_cache_new (gsize size);
+
+void grl_tracker_item_cache_free (GrlTrackerItemCache *cache);
+
+void grl_tracker_item_cache_add_item (GrlTrackerItemCache *cache,
+				      guint id,
+				      GrlTrackerSource *source);
+
+GrlTrackerSource *grl_tracker_item_cache_get_source (GrlTrackerItemCache *cache,
+						     guint id);
+
+#endif /* _GRL_TRACKER_UTILS_H_ */
diff --git a/src/tracker/grl-tracker.c b/src/tracker/grl-tracker.c
index 4a9dfef..2ac5254 100644
--- a/src/tracker/grl-tracker.c
+++ b/src/tracker/grl-tracker.c
@@ -33,6 +33,7 @@
 #include <tracker-sparql.h>
 
 #include "grl-tracker.h"
+#include "grl-tracker-utils.h"
 
 /* --------- Logging  -------- */
 
@@ -51,6 +52,8 @@ GRL_LOG_DOMAIN_STATIC(tracker_log_domain);
 #define RDF_TYPE_VIDEO  "nmm#Video"
 #define RDF_TYPE_BOX    "grilo#Box"
 
+#define TRACKER_ITEM_CACHE_SIZE (10000)
+
 /* ---- Plugin information --- */
 
 #define PLUGIN_ID   TRACKER_PLUGIN_ID
@@ -82,6 +85,13 @@ enum {
 
 #define TRACKER_MOUNTED_DATASOURCES_END " ))} GROUP BY (?datasource)"
 
+#define TRACKER_SOURCE_ITEM_START                                       \
+  "SELECT rdf:type(?urn) tracker:id(?urn) nie:dataSource(?urn) "	\
+  "WHERE { ?urn a nfo:FileDataObject . "                                \
+  "FILTER (tracker:id(?urn) IN ("
+
+#define TRACKER_SOURCE_ITEM_END ")) }"
+
 #define TRACKER_DATASOURCES_REQUEST                                     \
   "SELECT ?urn nie:dataSource(?urn) AS ?source "                        \
   "(SELECT GROUP_CONCAT(nie:url(tracker:mountPoint(?ds)), \",\") "      \
@@ -137,15 +147,27 @@ typedef struct {
 } tracker_grl_sparql_t;
 
 typedef struct {
-  gboolean in_use;
-
+  /* tables of items we know the source */
+  GHashTable *inserted_items;
+  GHashTable *deleted_items;
   GHashTable *updated_items;
-  GList *updated_items_list;
-  GList *updated_items_iter;
 
-  /* GList *updated_sources; */
+  /* table of items we don't know the source */
+  GHashTable *orphan_items;
 
-  TrackerSparqlCursor *cursor;
+  /* Global list of all impacted items, used to figure out which
+     sources have been added/removed. */
+  GHashTable *altered_items;
+  GList *altered_items_list;
+  GList *altered_items_iter;
+
+  /* List of new sources */
+  GList *new_sources;
+
+  /* Convenient stuff (for tracker/own callbacks...) */
+  TrackerSparqlCursor      *cursor;
+  gboolean                  in_use;
+  GrlMediaSourceChangeType  change_type;
 } tracker_evt_update_t;
 
 struct OperationSpec {
@@ -173,6 +195,7 @@ struct _GrlTrackerSourcePriv {
   GHashTable *operations;
 
   gchar *tracker_datasource;
+  gboolean notify_changes;
 };
 
 #define GRL_TRACKER_SOURCE_GET_PRIVATE(object)		\
@@ -212,6 +235,12 @@ static void grl_tracker_source_browse (GrlMediaSource *source,
 static void grl_tracker_source_cancel (GrlMediaSource *source,
                                        guint operation_id);
 
+static gboolean grl_tracker_source_change_start (GrlMediaSource *source,
+						 GError **error);
+
+static gboolean grl_tracker_source_change_stop (GrlMediaSource *source,
+						GError **error);
+
 static gchar *get_tracker_source_name (const gchar *uri,
                                        const gchar *datasource);
 
@@ -227,16 +256,79 @@ static TrackerSparqlConnection *tracker_connection = NULL;
 static gboolean tracker_per_device_source = FALSE;
 static const GrlPluginInfo *tracker_grl_plugin;
 static guint tracker_dbus_signal_id = 0;
+static GrlTrackerItemCache *tracker_item_cache;
 
 /* =================== Tracker Plugin  =============== */
 
+static void
+grl_tracker_add_source (GrlTrackerSource *source)
+{
+  grl_plugin_registry_register_source (grl_plugin_registry_get_default (),
+                                       tracker_grl_plugin,
+                                       GRL_MEDIA_PLUGIN (source),
+                                       NULL);
+}
+
+static void
+grl_tracker_del_source (GrlTrackerSource *source)
+{
+  grl_plugin_registry_unregister_source (grl_plugin_registry_get_default (),
+                                         GRL_MEDIA_PLUGIN (source),
+                                         NULL);
+}
+
+/* Builds an appropriate GrlMedia based on ontology type returned by
+   tracker, or NULL if unknown */
+static GrlMedia *
+build_grilo_media (const gchar *rdf_type)
+{
+  GrlMedia *media = NULL;
+  gchar **rdf_single_type;
+  int i;
+
+  if (!rdf_type) {
+    return NULL;
+  }
+
+  /* As rdf_type can be formed by several types, split them */
+  rdf_single_type = g_strsplit (rdf_type, ",", -1);
+  i = g_strv_length (rdf_single_type) - 1;
+
+  while (!media && i >= 0) {
+    if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_MUSIC)) {
+      media = grl_media_audio_new ();
+    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_VIDEO)) {
+      media = grl_media_video_new ();
+    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_IMAGE)) {
+      media = grl_media_image_new ();
+    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_ARTIST)) {
+      media = grl_media_box_new ();
+    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_ALBUM)) {
+      media = grl_media_box_new ();
+    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_BOX)) {
+      media = grl_media_box_new ();
+    }
+    i--;
+  }
+
+  g_strfreev (rdf_single_type);
+
+  return media;
+}
+
 static tracker_evt_update_t *
 tracker_evt_update_new (void)
 {
   tracker_evt_update_t *evt = g_slice_new0 (tracker_evt_update_t);
 
+  evt->altered_items = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+  evt->inserted_items = g_hash_table_new (g_direct_hash, g_direct_equal);
+  evt->deleted_items = g_hash_table_new (g_direct_hash, g_direct_equal);
   evt->updated_items = g_hash_table_new (g_direct_hash, g_direct_equal);
 
+  evt->orphan_items = g_hash_table_new (g_direct_hash, g_direct_equal);
+
   return evt;
 }
 
@@ -246,8 +338,16 @@ tracker_evt_update_free (tracker_evt_update_t *evt)
   if (!evt)
     return;
 
+  g_hash_table_destroy (evt->altered_items);
+  g_list_free (evt->altered_items_list);
+
+  g_hash_table_destroy (evt->inserted_items);
+  g_hash_table_destroy (evt->deleted_items);
   g_hash_table_destroy (evt->updated_items);
-  g_list_free (evt->updated_items_list);
+
+  g_hash_table_destroy (evt->orphan_items);
+
+  g_list_free (evt->new_sources);
 
   if (evt->cursor != NULL)
     g_object_unref (evt->cursor);
@@ -256,26 +356,193 @@ tracker_evt_update_free (tracker_evt_update_t *evt)
 }
 
 static void
-grl_tracker_add_source (GrlTrackerSource *source)
+tracker_evt_postupdate_sources (tracker_evt_update_t *evt)
 {
-  grl_plugin_registry_register_source (grl_plugin_registry_get_default (),
-                                       tracker_grl_plugin,
-                                       GRL_MEDIA_PLUGIN (source),
-                                       NULL);
+  GList *new_source = evt->new_sources;
+
+  while (new_source != NULL) {
+    grl_tracker_add_source (GRL_TRACKER_SOURCE (new_source->data));
+    new_source = new_source->next;
+  }
+
+  tracker_evt_update_free (evt);
 }
 
 static void
-grl_tracker_del_source (GrlTrackerSource *source)
+tracker_evt_update_orphan_item_cb (GObject              *object,
+                                   GAsyncResult         *result,
+                                   tracker_evt_update_t *evt)
 {
-  grl_plugin_registry_unregister_source (grl_plugin_registry_get_default (),
-                                         GRL_MEDIA_PLUGIN (source),
-                                         NULL);
+  guint id;
+  const gchar *datasource;
+  GrlMediaPlugin *plugin;
+  GError *tracker_error = NULL;
+
+  GRL_DEBUG ("%s", __FUNCTION__);
+
+  if (!tracker_sparql_cursor_next_finish (evt->cursor,
+                                          result,
+                                          &tracker_error)) {
+    if (tracker_error != NULL) {
+      GRL_DEBUG ("\terror in parsing : %s", tracker_error->message);
+      g_error_free (tracker_error);
+    } else {
+      GRL_DEBUG ("\tend of parsing...");
+    }
+
+    /* Once all items have been processed, add new sources and we're
+       done. */
+    tracker_evt_postupdate_sources (evt);
+
+    return;
+  }
+
+  id = tracker_sparql_cursor_get_integer (evt->cursor, 1);
+  datasource = tracker_sparql_cursor_get_string (evt->cursor, 2, NULL);
+
+  plugin = grl_plugin_registry_lookup_source (grl_plugin_registry_get_default (),
+                                              datasource);
+
+  if (plugin) {
+    GrlMedia *media =
+      build_grilo_media (tracker_sparql_cursor_get_string (evt->cursor, 0, NULL));
+    gchar *str_id = g_strdup_printf ("%i", id);
+    gint change_type =
+      GPOINTER_TO_INT (g_hash_table_lookup (evt->orphan_items,
+					    GSIZE_TO_POINTER (id)));
+
+    grl_data_set_string (GRL_DATA (media), GRL_METADATA_KEY_ID, str_id);
+
+    GRL_DEBUG ("Update op=%i item=%i", change_type, id);
+
+    grl_media_source_notify_change (GRL_MEDIA_SOURCE (plugin), media,
+                                    change_type, FALSE);
+
+    g_free (str_id);
+  }
+}
+
+static void
+tracker_evt_update_orphans_cb (GObject              *object,
+                               GAsyncResult         *result,
+                               tracker_evt_update_t *evt)
+{
+  GError *tracker_error = NULL;
+
+  GRL_DEBUG ("%s", __FUNCTION__);
+
+  if (evt->cursor != NULL)
+    g_object_unref (evt->cursor);
+  evt->cursor = tracker_sparql_connection_query_finish (tracker_connection,
+                                                        result, NULL);
+
+  if (tracker_error != NULL) {
+    GRL_WARNING ("Could not execute sparql query: %s", tracker_error->message);
+
+    g_error_free (tracker_error);
+    tracker_evt_update_free (evt); /* TODO: change order with
+                                      sources/items parsing */
+    return;
+  }
+
+  tracker_sparql_cursor_next_async (evt->cursor, NULL,
+                                    (GAsyncReadyCallback) tracker_evt_update_orphan_item_cb,
+                                    (gpointer) evt);
+}
+
+static void
+tracker_evt_update_orphans (tracker_evt_update_t *evt)
+{
+  GString *request_str;
+  GList *subject, *subjects;
+
+  GRL_DEBUG ("%s", __FUNCTION__);
+
+  if (g_hash_table_size (evt->orphan_items) < 1)
+    return;
+
+  request_str = g_string_new (TRACKER_SOURCE_ITEM_START);
+  subjects = g_hash_table_get_keys (evt->orphan_items);
+  subject = subjects;
+
+  g_string_append_printf (request_str, "%i", GPOINTER_TO_INT (subject->data));
+  subject = subject->next;
+
+  while (subject != NULL) {
+    g_string_append_printf (request_str, ", %i",
+                            GPOINTER_TO_INT (subject->data));
+    subject = subject->next;
+  }
+  g_list_free (subjects);
+
+  g_string_append (request_str, TRACKER_SOURCE_ITEM_END);
+
+  GRL_DEBUG ("\trequest : '%s'", request_str->str);
+
+  tracker_sparql_connection_query_async (tracker_connection, request_str->str,
+                                         NULL, (GAsyncReadyCallback) tracker_evt_update_orphans_cb,
+                                         evt);
+
+  g_string_free (request_str, TRUE);
+}
+
+static void
+tracker_evt_update_items_cb (gpointer              key,
+                             gpointer              value,
+                             tracker_evt_update_t *evt)
+{
+  guint id = GPOINTER_TO_INT (key);
+  gchar *str_id;
+  GrlTrackerSource *source = (GrlTrackerSource *) value;
+  GrlTrackerSourcePriv *priv;
+  GrlMedia *media;
+
+  GRL_DEBUG ("%s", __FUNCTION__);
+
+  if (!source) {
+    g_assert ("not in cache ???");
+    return;
+  }
+
+  priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+  if (!priv->notify_changes) {
+    GRL_DEBUG ("no notification for source %s...",
+	       grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+    return;
+  }
+
+  media = grl_media_new ();
+  str_id = g_strdup_printf ("%i", id);
+  grl_data_set_string (GRL_DATA (media), GRL_METADATA_KEY_ID, str_id);
+
+  GRL_DEBUG ("Update op=%i item=%i on source %s", evt->change_type, id,
+	     grl_metadata_source_get_name (GRL_METADATA_SOURCE (source)));
+
+  grl_media_source_notify_change (GRL_MEDIA_SOURCE (source), media,
+                                  evt->change_type, FALSE);
+
+  g_free (str_id);
 }
 
 static void
-tracker_evt_update_process_item_cb (GObject              *object,
-                                    GAsyncResult         *result,
-                                    tracker_evt_update_t *evt)
+tracker_evt_update_items (tracker_evt_update_t *evt)
+{
+  evt->change_type = GRL_CONTENT_REMOVED;
+  g_hash_table_foreach (evt->deleted_items,
+                        (GHFunc) tracker_evt_update_items_cb, evt);
+  evt->change_type = GRL_CONTENT_ADDED;
+  g_hash_table_foreach (evt->inserted_items,
+                        (GHFunc) tracker_evt_update_items_cb, evt);
+  evt->change_type = GRL_CONTENT_CHANGED;
+  g_hash_table_foreach (evt->updated_items,
+                        (GHFunc) tracker_evt_update_items_cb, evt);
+}
+
+static void
+tracker_evt_preupdate_sources_item_cb (GObject              *object,
+                                       GAsyncResult         *result,
+                                       tracker_evt_update_t *evt)
 {
   const gchar *datasource, *uri;
   gboolean source_mounted;
@@ -292,10 +559,14 @@ tracker_evt_update_process_item_cb (GObject              *object,
       GRL_DEBUG ("\terror in parsing : %s", tracker_error->message);
       g_error_free (tracker_error);
     } else {
-      GRL_DEBUG ("\tend of parsing :)");
+      GRL_DEBUG ("\tend of parsing... start notifying sources");
     }
 
-    tracker_evt_update_free (evt);
+    /* Once all sources have been preupdated, start items
+       updates. */
+    tracker_evt_update_items (evt);
+    tracker_evt_update_orphans (evt);
+
     return;
   }
 
@@ -319,7 +590,8 @@ tracker_evt_update_process_item_cb (GObject              *object,
                              "source-desc", SOURCE_DESC,
                              "tracker-connection", tracker_connection,
                              NULL);
-      grl_tracker_add_source (GRL_TRACKER_SOURCE (plugin));
+      /* Defer source creation until we have processed all sources */
+      evt->new_sources = g_list_append (evt->new_sources, plugin);
       g_free (source_name);
     } else {
       GRL_DEBUG ("\tChanges on source %p / %s", plugin, datasource);
@@ -329,19 +601,21 @@ tracker_evt_update_process_item_cb (GObject              *object,
   }
 
   tracker_sparql_cursor_next_async (evt->cursor, NULL,
-                                    (GAsyncReadyCallback) tracker_evt_update_process_item_cb,
+                                    (GAsyncReadyCallback) tracker_evt_preupdate_sources_item_cb,
                                     (gpointer) evt);
 }
 
 static void
-tracker_evt_update_process_cb (GObject              *object,
-                               GAsyncResult         *result,
-                               tracker_evt_update_t *evt)
+tracker_evt_preupdate_sources_cb (GObject              *object,
+                                  GAsyncResult         *result,
+                                  tracker_evt_update_t *evt)
 {
   GError *tracker_error = NULL;
 
   GRL_DEBUG ("%s", __FUNCTION__);
 
+  if (evt->cursor != NULL)
+    g_object_unref (evt->cursor);
   evt->cursor = tracker_sparql_connection_query_finish (tracker_connection,
                                                         result, NULL);
 
@@ -354,37 +628,38 @@ tracker_evt_update_process_cb (GObject              *object,
   }
 
   tracker_sparql_cursor_next_async (evt->cursor, NULL,
-                                    (GAsyncReadyCallback) tracker_evt_update_process_item_cb,
+                                    (GAsyncReadyCallback) tracker_evt_preupdate_sources_item_cb,
                                     (gpointer) evt);
 }
 
 static void
-tracker_evt_update_process (tracker_evt_update_t *evt)
+tracker_evt_preupdate_sources (tracker_evt_update_t *evt)
 {
   GString *request_str = g_string_new (TRACKER_MOUNTED_DATASOURCES_START);
 
   GRL_DEBUG ("%s", __FUNCTION__);
 
-  evt->updated_items_iter = evt->updated_items_list;
+  evt->altered_items_iter = evt->altered_items_list;
   g_string_append_printf (request_str, "%i",
-                          GPOINTER_TO_INT (evt->updated_items_iter->data));
-  evt->updated_items_iter = evt->updated_items_iter->next;
+                          GPOINTER_TO_INT (evt->altered_items_iter->data));
+  evt->altered_items_iter = evt->altered_items_iter->next;
 
-  while (evt->updated_items_iter != NULL) {
+  while (evt->altered_items_iter != NULL) {
     g_string_append_printf (request_str, ", %i",
-                            GPOINTER_TO_INT (evt->updated_items_iter->data));
-    evt->updated_items_iter = evt->updated_items_iter->next;
+                            GPOINTER_TO_INT (evt->altered_items_iter->data));
+    evt->altered_items_iter = evt->altered_items_iter->next;
   }
 
   g_string_append (request_str, TRACKER_MOUNTED_DATASOURCES_END);
 
-  GRL_DEBUG ("\trequest : %s", request_str->str);
+  GRL_DEBUG ("\trequest : '%s'", request_str->str);
 
   tracker_sparql_connection_query_async (tracker_connection,
                                          request_str->str,
                                          NULL,
-                                         (GAsyncReadyCallback) tracker_evt_update_process_cb,
+                                         (GAsyncReadyCallback) tracker_evt_preupdate_sources_cb,
                                          evt);
+
   g_string_free (request_str, TRUE);
 }
 
@@ -412,48 +687,95 @@ tracker_dbus_signal_cb (GDBusConnection *connection,
              (unsigned long) g_variant_iter_n_children (iter1),
              (unsigned long) g_variant_iter_n_children (iter2));
 
+  /* Process deleted items */
   while (g_variant_iter_loop (iter1, "(iiii)", &graph,
                               &subject, &predicate, &object)) {
-    subject_state = GPOINTER_TO_INT (g_hash_table_lookup (evt->updated_items,
-                                                          GSIZE_TO_POINTER (subject)));
+    gpointer psubject = GSIZE_TO_POINTER (subject);
+    GrlTrackerSource *source = grl_tracker_item_cache_get_source (tracker_item_cache,
+                                                                  subject);
+
+    GRL_DEBUG ("delete=> subject=%i", subject);
+
+    if (source) {
+      g_hash_table_insert (evt->deleted_items, psubject, source);
+    } else {
+      g_hash_table_insert (evt->orphan_items, psubject,
+                           GSIZE_TO_POINTER (GRL_CONTENT_REMOVED));
+    }
+
+    subject_state = GPOINTER_TO_INT (g_hash_table_lookup (evt->altered_items,
+                                                          psubject));
 
     if (subject_state == 0) {
-      g_hash_table_insert (evt->updated_items,
-                           GSIZE_TO_POINTER (subject),
-                           GSIZE_TO_POINTER (1));
-      evt->updated_items_list = g_list_append (evt->updated_items_list,
-                                               GSIZE_TO_POINTER (subject));
-    } else if (subject_state == 2)
-      evt->updated_items_list = g_list_append (evt->updated_items_list,
-                                               GSIZE_TO_POINTER (subject));
+
+      g_hash_table_insert (evt->altered_items, psubject, GSIZE_TO_POINTER (1));
+      evt->altered_items_list = g_list_append (evt->altered_items_list,
+                                               psubject);
+    } else if (subject_state == 2) {
+      evt->altered_items_list = g_list_append (evt->altered_items_list,
+                                               psubject);
+    }
   }
   g_variant_iter_free (iter1);
 
-
+  /* Process inserted items */
   while (g_variant_iter_loop (iter2, "(iiii)", &graph,
                               &subject, &predicate, &object)) {
-    subject_state = GPOINTER_TO_INT (g_hash_table_lookup (evt->updated_items,
+    gpointer psubject = GSIZE_TO_POINTER (subject);
+    GrlTrackerSource *source = grl_tracker_item_cache_get_source (tracker_item_cache,
+                                                                  subject);
+
+    GRL_DEBUG ("insert=> subject=%i", subject);
+
+    if (source) {
+      /* Removed & inserted items are probably just renamed items... */
+      if (g_hash_table_lookup (evt->deleted_items, psubject)) {
+        g_hash_table_remove (evt->deleted_items, psubject);
+        g_hash_table_insert (evt->updated_items, psubject, source);
+      } else if (!g_hash_table_lookup (evt->updated_items, psubject)) {
+        g_hash_table_insert (evt->inserted_items, psubject, source);
+      }
+    } else {
+      gpointer state;
+
+      if (g_hash_table_lookup_extended (evt->orphan_items, psubject,
+                                        NULL, &state) &&
+	  (GPOINTER_TO_INT (state) == GRL_CONTENT_REMOVED)) {
+        g_hash_table_insert (evt->orphan_items, psubject,
+                             GSIZE_TO_POINTER (GRL_CONTENT_CHANGED));
+      } else {
+        g_hash_table_insert (evt->orphan_items, psubject,
+                             GSIZE_TO_POINTER (GRL_CONTENT_ADDED));
+      }
+    }
+
+    subject_state = GPOINTER_TO_INT (g_hash_table_lookup (evt->altered_items,
                                                           GSIZE_TO_POINTER (subject)));
 
     if (subject_state == 0) {
-      g_hash_table_insert (evt->updated_items,
+      g_hash_table_insert (evt->altered_items,
                            GSIZE_TO_POINTER (subject),
                            GSIZE_TO_POINTER (1));
-      evt->updated_items_list = g_list_append (evt->updated_items_list,
-                                              GSIZE_TO_POINTER (subject));
+      evt->altered_items_list = g_list_append (evt->altered_items_list,
+                                               GSIZE_TO_POINTER (subject));
     } else if (subject_state == 2)
-      evt->updated_items_list = g_list_append (evt->updated_items_list,
-                                              GSIZE_TO_POINTER (subject));
+      evt->altered_items_list = g_list_append (evt->altered_items_list,
+                                               GSIZE_TO_POINTER (subject));
   }
   g_variant_iter_free (iter2);
 
-  evt->updated_items_iter = evt->updated_items_list;
+  evt->altered_items_iter = evt->altered_items_list;
 
-  GRL_DEBUG ("\t%u elements updated", g_hash_table_size (evt->updated_items));
+  GRL_DEBUG ("\tinserted=%i deleted=%i updated=%i orphan=%i changed=%i",
+             g_hash_table_size (evt->inserted_items),
+             g_hash_table_size (evt->deleted_items),
+             g_hash_table_size (evt->updated_items),
+	     g_hash_table_size (evt->orphan_items),
+             g_hash_table_size (evt->altered_items));
   GRL_DEBUG ("\t%u elements updated (list)",
-             g_list_length (evt->updated_items_list));
+             g_list_length (evt->altered_items_list));
 
-  tracker_evt_update_process (evt);
+  tracker_evt_preupdate_sources (evt);
 }
 
 static void
@@ -512,10 +834,7 @@ tracker_get_datasource_cb (GObject             *object,
                            "source-desc", SOURCE_DESC,
                            "tracker-connection", tracker_connection,
                            NULL);
-    grl_plugin_registry_register_source (grl_plugin_registry_get_default (),
-                                         tracker_grl_plugin,
-                                         GRL_MEDIA_PLUGIN (source),
-                                         NULL);
+    grl_tracker_add_source (source);
     g_free (source_name);
   }
 
@@ -586,6 +905,7 @@ grl_tracker_plugin_init (GrlPluginRegistry *registry,
   GRL_DEBUG ("%s", __FUNCTION__);
 
   tracker_grl_plugin = plugin;
+  tracker_item_cache = grl_tracker_item_cache_new (TRACKER_ITEM_CACHE_SIZE);
 
   if (!configs) {
     GRL_WARNING ("Configuration not provided! Using default configuration.");
@@ -635,11 +955,13 @@ grl_tracker_source_class_init (GrlTrackerSourceClass * klass)
   GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
   GObjectClass           *g_class        = G_OBJECT_CLASS (klass);
 
-  source_class->query    = grl_tracker_source_query;
-  source_class->metadata = grl_tracker_source_metadata;
-  source_class->search   = grl_tracker_source_search;
-  source_class->browse   = grl_tracker_source_browse;
-  source_class->cancel   = grl_tracker_source_cancel;
+  source_class->query               = grl_tracker_source_query;
+  source_class->metadata            = grl_tracker_source_metadata;
+  source_class->search              = grl_tracker_source_search;
+  source_class->browse              = grl_tracker_source_browse;
+  source_class->cancel              = grl_tracker_source_cancel;
+  source_class->notify_change_start = grl_tracker_source_change_start;
+  source_class->notify_change_stop  = grl_tracker_source_change_stop;
 
   metadata_class->supported_keys = grl_tracker_source_supported_keys;
 
@@ -942,47 +1264,9 @@ get_select_string (GrlMediaSource *source, const GList *keys)
   return g_string_free (gstr, FALSE);
 }
 
-/* Builds an appropriate GrlMedia based on ontology type returned by tracker, or
-   NULL if unknown */
-static GrlMedia *
-build_grilo_media (const gchar *rdf_type)
-{
-  GrlMedia *media = NULL;
-  gchar **rdf_single_type;
-  int i;
-
-  if (!rdf_type) {
-    return NULL;
-  }
-
-  /* As rdf_type can be formed by several types, split them */
-  rdf_single_type = g_strsplit (rdf_type, ",", -1);
-  i = g_strv_length (rdf_single_type) - 1;
-
-  while (!media && i >= 0) {
-    if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_MUSIC)) {
-      media = grl_media_audio_new ();
-    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_VIDEO)) {
-      media = grl_media_video_new ();
-    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_IMAGE)) {
-      media = grl_media_image_new ();
-    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_ARTIST)) {
-      media = grl_media_box_new ();
-    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_ALBUM)) {
-      media = grl_media_box_new ();
-    } else if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_BOX)) {
-      media = grl_media_box_new ();
-    }
-    i--;
-  }
-
-  g_strfreev (rdf_single_type);
-
-  return media;
-}
-
 static void
-fill_grilo_media_from_sparql (GrlMedia            *media,
+fill_grilo_media_from_sparql (GrlTrackerSource    *source,
+                              GrlMedia            *media,
                               TrackerSparqlCursor *cursor,
                               gint                 column)
 {
@@ -1015,6 +1299,12 @@ fill_grilo_media_from_sparql (GrlMedia            *media,
 
   switch (G_PARAM_SPEC (assoc->grl_key)->value_type) {
   case G_TYPE_STRING:
+    /* Cache the source associated to this result. */
+    if (assoc->grl_key == GRL_METADATA_KEY_ID) {
+      grl_tracker_item_cache_add_item (tracker_item_cache,
+                                       tracker_sparql_cursor_get_integer (cursor, column),
+                                       source);
+    }
     val.str_val = tracker_sparql_cursor_get_string (cursor, column, NULL);
     if (val.str_val != NULL)
       grl_data_set_string (GRL_DATA (media), assoc->grl_key, val.str_val);
@@ -1102,7 +1392,8 @@ tracker_query_result_cb (GObject              *source_object,
     for (col = 1 ;
          col < tracker_sparql_cursor_get_n_columns (operation->cursor) ;
          col++) {
-      fill_grilo_media_from_sparql (media, operation->cursor, col);
+      fill_grilo_media_from_sparql (GRL_TRACKER_SOURCE (operation->source),
+                                    media, operation->cursor, col);
     }
 
     operation->callback (operation->source,
@@ -1197,7 +1488,8 @@ tracker_metadata_cb (GObject                    *source_object,
 
   /* Translate Sparql result into Grilo result */
   for (col = 0 ; col < tracker_sparql_cursor_get_n_columns (cursor) ; col++) {
-    fill_grilo_media_from_sparql (ms->media, cursor, col);
+    fill_grilo_media_from_sparql (GRL_TRACKER_SOURCE (ms->source),
+                                  ms->media, cursor, col);
   }
 
   ms->callback (ms->source, ms->media, ms->user_data, NULL);
@@ -1333,7 +1625,7 @@ grl_tracker_source_query (GrlMediaSource *source,
     return;
   }
 
-  GRL_DEBUG ("select : %s", qs->query);
+  GRL_DEBUG ("select : '%s'", qs->query);
 
   os = tracker_operation_initiate (source, priv, qs->query_id);
   os->keys         = qs->keys;
@@ -1505,3 +1797,23 @@ grl_tracker_source_cancel (GrlMediaSource *source, guint operation_id)
   if (os != NULL)
     g_cancellable_cancel (os->cancel_op);
 }
+
+static gboolean
+grl_tracker_source_change_start (GrlMediaSource *source, GError **error)
+{
+  GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+  priv->notify_changes = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+grl_tracker_source_change_stop (GrlMediaSource *source, GError **error)
+{
+  GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+  priv->notify_changes = FALSE;
+
+  return TRUE;
+}
-- 
1.7.2.3



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