summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2020-05-05 19:00:30 +0200
committerJean Felder <jean.felder@gmail.com>2020-08-21 15:05:10 +0000
commit89975911057d99eb639add25e348558672771eb5 (patch)
tree707508e567c0224087dad9d0fceada5681d1be5f
parent5511ad119f89853312d893ffcf286ef00a2c0418 (diff)
downloadgrilo-plugins-89975911057d99eb639add25e348558672771eb5.tar.gz
plugins: Add tracker3 plugin
This is a separate plugin as it requires a version bump, different queries, and can do some niceties supported in the new version.
-rw-r--r--meson.build2
-rw-r--r--meson_options.txt1
-rw-r--r--src/tracker3/grl-tracker-request-queue.c227
-rw-r--r--src/tracker3/grl-tracker-request-queue.h84
-rw-r--r--src/tracker3/grl-tracker-source-api.c1468
-rw-r--r--src/tracker3/grl-tracker-source-api.h77
-rw-r--r--src/tracker3/grl-tracker-source-cache.c190
-rw-r--r--src/tracker3/grl-tracker-source-cache.h47
-rw-r--r--src/tracker3/grl-tracker-source-notif.c234
-rw-r--r--src/tracker3/grl-tracker-source-notif.h55
-rw-r--r--src/tracker3/grl-tracker-source-priv.h87
-rw-r--r--src/tracker3/grl-tracker-source.c316
-rw-r--r--src/tracker3/grl-tracker-source.h97
-rw-r--r--src/tracker3/grl-tracker-utils.c879
-rw-r--r--src/tracker3/grl-tracker-utils.h95
-rw-r--r--src/tracker3/grl-tracker.c316
-rw-r--r--src/tracker3/grl-tracker.h37
-rw-r--r--src/tracker3/meson.build37
18 files changed, 4249 insertions, 0 deletions
diff --git a/meson.build b/meson.build
index f858a20..1713d02 100644
--- a/meson.build
+++ b/meson.build
@@ -77,6 +77,7 @@ sqlite3_dep = dependency('sqlite3', required: false)
totem_plparser_dep = dependency('totem-plparser', version: '>= 3.4.1', required: false)
totem_plparser_mini_dep = dependency('totem-plparser-mini', version: '>= 3.4.1', required: false)
tracker_sparql_dep = dependency('tracker-sparql-2.0', version: '>= 2.3.0', required: false)
+tracker3_dep = dependency('tracker-sparql-3.0', required: false)
lua_dep = dependency('lua', version: '>= 5.3.0', required: false)
if not lua_dep.found()
@@ -128,6 +129,7 @@ plugins = [ # NAME, REQ_DEPS, OPT_DEPS
['thetvdb', [grilo_net_dep, libxml_dep, libarchive_dep, gom_dep], []],
['tmdb', [json_glib_dep, libsoup_dep, grilo_net_dep], []],
['tracker', [tracker_sparql_dep], []],
+ ['tracker3', [tracker3_dep], []],
['vimeo', [grilo_net_dep, libxml_dep, totem_plparser_dep], []],
['youtube', [grilo_net_dep, libxml_dep, libgdata_dep, totem_plparser_dep], []],
]
diff --git a/meson_options.txt b/meson_options.txt
index ec324f6..77558ae 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -20,5 +20,6 @@ option('enable-shoutcast', type: 'combo', choices: [ 'auto', 'yes', 'no' ], valu
option('enable-thetvdb', type: 'combo', choices: [ 'auto', 'yes', 'no' ], value: 'auto', description: 'Enable Thetvdb plugin')
option('enable-tmdb', type: 'combo', choices: [ 'auto', 'yes', 'no' ], value: 'auto', description: 'Enable TMDb plugin')
option('enable-tracker', type: 'combo', choices: [ 'auto', 'yes', 'no' ], value: 'auto', description: 'Enable Tracker plugin')
+option('enable-tracker3', type: 'combo', choices: [ 'auto', 'yes', 'no' ], value: 'auto', description: 'Enable Tracker3 plugin')
option('enable-vimeo', type: 'combo', choices: [ 'auto', 'yes', 'no' ], value: 'auto', description: 'Enable Vimeo plugin')
option('enable-youtube', type: 'combo', choices: [ 'auto', 'yes', 'no' ], value: 'auto', description: 'Enable YouTube plugin')
diff --git a/src/tracker3/grl-tracker-request-queue.c b/src/tracker3/grl-tracker-request-queue.c
new file mode 100644
index 0000000..fb7d59e
--- /dev/null
+++ b/src/tracker3/grl-tracker-request-queue.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "grl-tracker.h"
+#include "grl-tracker-request-queue.h"
+
+/**/
+
+struct _GrlTrackerQueue {
+ GList *head;
+ GList *tail;
+ GHashTable *operations;
+ GHashTable *operations_ids;
+};
+
+/**/
+
+static void
+grl_tracker_op_terminate (GrlTrackerOp *os)
+{
+ if (os == NULL)
+ return;
+
+ g_clear_object (&os->cursor);
+ g_object_unref (os->cancel);
+ g_free (os->request);
+
+ g_slice_free (GrlTrackerOp, os);
+}
+
+static GrlTrackerOp *
+grl_tracker_op_initiate (gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GrlTrackerOp *os = g_slice_new0 (GrlTrackerOp);
+
+ os->request = request;
+ os->callback = callback;
+ os->data = data;
+ os->cancel = g_cancellable_new ();
+
+ return os;
+}
+
+GrlTrackerOp *
+grl_tracker_op_initiate_query (guint operation_id,
+ gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GrlTrackerOp *os = grl_tracker_op_initiate (request,
+ callback,
+ data);
+
+ os->type = GRL_TRACKER_OP_TYPE_QUERY;
+ os->operation_id = operation_id;
+
+ /* g_hash_table_insert (grl_tracker_operations, */
+ /* GSIZE_TO_POINTER (operation_id), os); */
+
+ return os;
+}
+
+GrlTrackerOp *
+grl_tracker_op_initiate_metadata (gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GrlTrackerOp *os = grl_tracker_op_initiate (request,
+ callback,
+ data);
+
+ os->type = GRL_TRACKER_OP_TYPE_QUERY;
+
+ return os;
+}
+
+GrlTrackerOp *
+grl_tracker_op_initiate_set_metadata (gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GrlTrackerOp *os = grl_tracker_op_initiate (request,
+ callback,
+ data);
+
+ os->type = GRL_TRACKER_OP_TYPE_UPDATE;
+
+ return os;
+}
+
+static void
+grl_tracker_op_start (GrlTrackerOp *os)
+{
+ switch (os->type) {
+ case GRL_TRACKER_OP_TYPE_QUERY:
+ tracker_sparql_connection_query_async (grl_tracker_connection,
+ os->request,
+ NULL,
+ os->callback,
+ os);
+ break;
+
+ case GRL_TRACKER_OP_TYPE_UPDATE:
+ tracker_sparql_connection_update_async (grl_tracker_connection,
+ os->request,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ os->callback,
+ os);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+/**/
+
+GrlTrackerQueue *
+grl_tracker_queue_new (void)
+{
+ GrlTrackerQueue *queue = g_new0 (GrlTrackerQueue, 1);
+
+ queue->operations = g_hash_table_new (g_direct_hash, g_direct_equal);
+ queue->operations_ids = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ return queue;
+}
+
+void
+grl_tracker_queue_push (GrlTrackerQueue *queue,
+ GrlTrackerOp *os)
+{
+ gboolean first = FALSE;
+
+ queue->tail = g_list_append (queue->tail, os);
+ if (queue->tail->next)
+ queue->tail = queue->tail->next;
+ else {
+ queue->head = queue->tail;
+ first = TRUE;
+ }
+
+ g_assert (queue->tail->next == NULL);
+
+ g_hash_table_insert (queue->operations, os, queue->tail);
+ if (os->operation_id != 0)
+ g_hash_table_insert (queue->operations_ids,
+ GSIZE_TO_POINTER (os->operation_id), os);
+
+ if (first)
+ grl_tracker_op_start (os);
+}
+
+void
+grl_tracker_queue_cancel (GrlTrackerQueue *queue,
+ GrlTrackerOp *os)
+{
+ GList *item = g_hash_table_lookup (queue->operations, os);
+
+ if (!item)
+ return;
+
+ g_cancellable_cancel (os->cancel);
+
+ g_hash_table_remove (queue->operations, os);
+ if (os->operation_id != 0)
+ g_hash_table_remove (queue->operations_ids,
+ GSIZE_TO_POINTER (os->operation_id));
+
+ if (item == queue->head) {
+ queue->head = queue->head->next;
+ }
+ if (item == queue->tail) {
+ queue->tail = queue->tail->prev;
+ }
+
+ if (item->prev)
+ item->prev->next = item->next;
+ if (item->next)
+ item->next->prev = item->prev;
+
+ item->next = NULL;
+ item->prev = NULL;
+ g_list_free (item);
+}
+
+void
+grl_tracker_queue_done (GrlTrackerQueue *queue,
+ GrlTrackerOp *os)
+{
+ GrlTrackerOp *next_os;
+
+ grl_tracker_queue_cancel (queue, os);
+ grl_tracker_op_terminate (os);
+
+ if (!queue->head)
+ return;
+
+ next_os = queue->head->data;
+
+ grl_tracker_op_start (next_os);
+}
diff --git a/src/tracker3/grl-tracker-request-queue.h b/src/tracker3/grl-tracker-request-queue.h
new file mode 100644
index 0000000..8805b9c
--- /dev/null
+++ b/src/tracker3/grl-tracker-request-queue.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_REQUEST_QUEUE_H_
+#define _GRL_TRACKER_REQUEST_QUEUE_H_
+
+#include <grilo.h>
+#include <tracker-sparql.h>
+
+/**/
+
+typedef enum {
+ GRL_TRACKER_OP_TYPE_QUERY,
+ GRL_TRACKER_OP_TYPE_UPDATE,
+} GrlTrackerOpType;
+
+typedef struct {
+ GrlTrackerOpType type;
+ GAsyncReadyCallback callback;
+ GCancellable *cancel;
+ TrackerSparqlConnection *connection;
+ gchar *request;
+ const GList *keys;
+ gpointer data;
+
+ TrackerSparqlCursor *cursor;
+
+ guint operation_id;
+
+ guint skip;
+ guint count;
+ guint current;
+ GrlTypeFilter type_filter;
+} GrlTrackerOp;
+
+typedef struct _GrlTrackerQueue GrlTrackerQueue;
+
+/**/
+
+GrlTrackerOp *grl_tracker_op_initiate_query (guint operation_id,
+ gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data);
+
+GrlTrackerOp *grl_tracker_op_initiate_metadata (gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data);
+
+GrlTrackerOp *grl_tracker_op_initiate_set_metadata (gchar *request,
+ GAsyncReadyCallback callback,
+ gpointer data);
+
+/**/
+
+GrlTrackerQueue *grl_tracker_queue_new (void);
+
+void grl_tracker_queue_push (GrlTrackerQueue *queue, GrlTrackerOp *os);
+
+void grl_tracker_queue_cancel (GrlTrackerQueue *queue, GrlTrackerOp *os);
+
+void grl_tracker_queue_done (GrlTrackerQueue *queue, GrlTrackerOp *os);
+
+#endif /* _GRL_TRACKER_REQUEST_QUEUE_H_ */
diff --git a/src/tracker3/grl-tracker-source-api.c b/src/tracker3/grl-tracker-source-api.c
new file mode 100644
index 0000000..5abf3a0
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-api.c
@@ -0,0 +1,1468 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gio/gio.h>
+#include <tracker-sparql.h>
+
+#include "grl-tracker.h"
+#include "grl-tracker-source-api.h"
+#include "grl-tracker-source-cache.h"
+#include "grl-tracker-source-priv.h"
+#include "grl-tracker-request-queue.h"
+#include "grl-tracker-utils.h"
+
+/* --------- Logging -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT tracker_source_request_log_domain
+
+GRL_LOG_DOMAIN_STATIC(tracker_source_request_log_domain);
+GRL_LOG_DOMAIN_STATIC(tracker_source_result_log_domain);
+
+/* Inputs/requests */
+#define GRL_IDEBUG(args...) \
+ GRL_LOG (tracker_source_request_log_domain, \
+ GRL_LOG_LEVEL_DEBUG, args)
+
+/* Outputs/results */
+#define GRL_ODEBUG(args...) \
+ GRL_LOG (tracker_source_result_log_domain, \
+ GRL_LOG_LEVEL_DEBUG, args)
+
+/* ------- Definitions ------- */
+
+#define TRACKER_QUERY_LIMIT \
+ "OFFSET %u " \
+ "LIMIT %u"
+
+#define TRACKER_QUERY_PARTIAL_REQUEST \
+ "SELECT rdf:type(?urn) %s " \
+ "WHERE { %s . %s } " \
+ "ORDER BY DESC(nfo:fileLastModified(?urn)) " \
+ TRACKER_QUERY_LIMIT
+
+#define TRACKER_QUERY_FULL_REQUEST \
+ "%s " \
+ TRACKER_QUERY_LIMIT
+
+#define TRACKER_SEARCH_REQUEST \
+ "SELECT DISTINCT rdf:type(?urn) %s " \
+ "WHERE " \
+ "{ " \
+ "%s " \
+ "?urn tracker:available ?tr . " \
+ "?urn fts:match \"%s\" . " \
+ "%s %s " \
+ "} " \
+ "ORDER BY DESC(nfo:fileLastModified(?urn)) " \
+ "OFFSET %u " \
+ "LIMIT %u"
+
+#define TRACKER_SEARCH_ALL_REQUEST \
+ "SELECT DISTINCT rdf:type(?urn) %s " \
+ "WHERE " \
+ "{ " \
+ "%s " \
+ "?urn tracker:available ?tr . " \
+ "%s %s " \
+ "} " \
+ "ORDER BY DESC(nfo:fileLastModified(?urn)) " \
+ "OFFSET %u " \
+ "LIMIT %u"
+
+#define TRACKER_BROWSE_SHOW_DOCUMENTS \
+ "{ ?urn a nfo:Document } UNION"
+
+#define TRACKER_BROWSE_CATEGORY_REQUEST \
+ "SELECT rdf:type(?urn) %s " \
+ "WHERE " \
+ "{ " \
+ "?urn a %s . " \
+ "?urn nie:isStoredAs ?file . " \
+ "?file tracker:available ?tr . " \
+ "%s " \
+ "%s " \
+ "} " \
+ "ORDER BY DESC(nfo:fileLastModified(?urn)) " \
+ "OFFSET %u " \
+ "LIMIT %u"
+
+#define TRACKER_BROWSE_FILESYSTEM_ROOT_REQUEST \
+ "SELECT DISTINCT rdf:type(?urn) %s " \
+ "WHERE " \
+ "{ " \
+ "%s " \
+ "{ ?urn a nfo:Folder } %s " \
+ "%s " \
+ "FILTER (!bound(nfo:belongsToContainer(?urn))) " \
+ "} " \
+ "ORDER BY DESC(nfo:fileLastModified(?urn)) " \
+ "OFFSET %u " \
+ "LIMIT %u"
+
+#define TRACKER_BROWSE_FILESYSTEM_REQUEST \
+ "SELECT DISTINCT rdf:type(?urn) %s " \
+ "WHERE " \
+ "{ " \
+ "%s " \
+ "{ ?urn a nfo:Folder } %s " \
+ "%s " \
+ "FILTER(tracker:id(nfo:belongsToContainer(?urn)) = %s) " \
+ "} " \
+ "ORDER BY DESC(nfo:fileLastModified(?urn)) " \
+ "OFFSET %u " \
+ "LIMIT %u"
+
+#define TRACKER_RESOLVE_REQUEST \
+ "SELECT %s " \
+ "WHERE " \
+ "{ " \
+ "?urn a nie:InformationElement ; " \
+ " nie:isStoredAs ?file . " \
+ "FILTER (tracker:id(?urn) = %s) " \
+ "}"
+
+#define TRACKER_RESOLVE_URL_REQUEST \
+ "SELECT %s " \
+ "WHERE " \
+ "{ " \
+ "?urn a nie:DataObject . " \
+ "?urn nie:url \"%s\" " \
+ "}"
+
+#define TRACKER_DELETE_REQUEST \
+ "DELETE { <%s> %s } WHERE { <%s> a nfo:Media . %s }"
+
+#define TRACKER_SAVE_REQUEST \
+ "DELETE { <%s> %s } WHERE { <%s> a nfo:Media . %s } " \
+ "INSERT { <%s> a nfo:Media ; %s . }"
+
+#define TRACKER_TEST_MEDIA_FROM_URI_REQUEST \
+ "SELECT ?urn " \
+ "WHERE " \
+ "{ " \
+ "?urn nie:url \"%s\" ; " \
+ "tracker:available true ; " \
+ "a nfo:Media . " \
+ "%s " \
+ "}"
+
+#define TRACKER_TEST_MEDIA_FROM_URI_REQUEST_WITH_DOCUMENTS \
+ "SELECT ?urn " \
+ "WHERE " \
+ "{ " \
+ "?urn nie:url \"%s\" ; " \
+ "tracker:available true . " \
+ "%s " \
+ "FILTER (?type IN ( nfo:Media, nfo:Document ))" \
+ "}"
+
+#define TRACKER_MEDIA_FROM_URI_REQUEST \
+ "SELECT rdf:type(?urn) %s " \
+ "WHERE " \
+ "{ " \
+ "?urn nie:url \"%s\" ; " \
+ "tracker:available ?tr . " \
+ "%s " \
+ "} " \
+
+/**/
+
+/**/
+
+static GrlKeyID grl_metadata_key_tracker_category;
+static GHashTable *grl_tracker_operations;
+
+/**/
+
+
+/**/
+
+/**/
+
+static void
+set_title_from_filename (GrlMedia *media)
+{
+ const gchar *url;
+ gchar *path, *display_name, *ext, *title;
+ guint suffix_len;
+
+ url = grl_media_get_url (media);
+ if (url == NULL)
+ return;
+
+ path = g_filename_from_uri (url, NULL, NULL);
+ if (!path)
+ return;
+ display_name = g_filename_display_basename (path);
+ g_free (path);
+ ext = strrchr (display_name, '.');
+ if (!ext)
+ goto out;
+
+ suffix_len = strlen (ext);
+ if (suffix_len != 4 && suffix_len != 5)
+ goto out;
+
+ title = g_strndup (display_name, ext - display_name);
+ if (g_strcmp0 (grl_media_get_title (media), title) == 0)
+ grl_data_set_boolean (GRL_DATA (media), GRL_METADATA_KEY_TITLE_FROM_FILENAME, TRUE);
+ g_free (title);
+
+out:
+ g_free (display_name);
+}
+
+static void
+fill_grilo_media_from_sparql (GrlTrackerSource *source,
+ GrlMedia *media,
+ TrackerSparqlCursor *cursor,
+ gint column)
+{
+ const gchar *sparql_key = tracker_sparql_cursor_get_variable_name (cursor,
+ column);
+ tracker_grl_sparql_t *assoc =
+ grl_tracker_get_mapping_from_sparql (sparql_key);
+ union {
+ gint64 int_val;
+ gdouble double_val;
+ const gchar *str_val;
+ } val;
+
+ GrlKeyID grl_key;
+
+ if (assoc == NULL) {
+ /* Maybe the user is setting the key */
+ GrlRegistry *registry = grl_registry_get_default ();
+ grl_key = grl_registry_lookup_metadata_key (registry, sparql_key);
+ if (grl_key == GRL_METADATA_KEY_INVALID) {
+ return;
+ }
+ } else {
+ grl_key = assoc->grl_key;
+ }
+
+ GRL_ODEBUG ("\tSetting media prop (col=%i/var=%s/prop=%s) %s",
+ column,
+ sparql_key,
+ GRL_METADATA_KEY_GET_NAME (grl_key),
+ tracker_sparql_cursor_get_string (cursor, column, NULL));
+
+ if (tracker_sparql_cursor_is_bound (cursor, column) == FALSE) {
+ GRL_ODEBUG ("\t\tDropping, no data");
+ return;
+ }
+
+ if (grl_data_has_key (GRL_DATA (media), grl_key)) {
+ GRL_ODEBUG ("\t\tDropping, already here");
+ return;
+ }
+
+ if (assoc && assoc->set_value) {
+ assoc->set_value (cursor, column, media, assoc->grl_key);
+ } else {
+ GType grl_type = GRL_METADATA_KEY_GET_TYPE (grl_key);
+ if (grl_type == G_TYPE_STRING) {
+ /* Cache the source associated to this result. */
+ if (grl_key == GRL_METADATA_KEY_ID) {
+ grl_tracker_source_cache_add_item (grl_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), grl_key, val.str_val);
+ } else if (grl_type == G_TYPE_INT) {
+ val.int_val = tracker_sparql_cursor_get_integer (cursor, column);
+ grl_data_set_int (GRL_DATA (media), grl_key, val.int_val);
+ } else if (grl_type == G_TYPE_INT64) {
+ val.int_val = tracker_sparql_cursor_get_integer (cursor, column);
+ grl_data_set_int64 (GRL_DATA (media), grl_key, val.int_val);
+ } else if (grl_type == G_TYPE_FLOAT) {
+ val.double_val = tracker_sparql_cursor_get_double (cursor, column);
+ grl_data_set_float (GRL_DATA (media), grl_key, (gfloat) val.double_val);
+ } else if (grl_type == G_TYPE_DATE_TIME) {
+ val.str_val = tracker_sparql_cursor_get_string (cursor, column, NULL);
+ GDateTime *date_time = grl_date_time_from_iso8601 (val.str_val);
+ grl_data_set_boxed (GRL_DATA (media), grl_key, date_time);
+ g_date_time_unref (date_time);
+ } else {
+ GRL_ODEBUG ("\t\tUnexpected data type");
+ }
+ }
+}
+
+static gchar *
+get_sparql_type_filter (GrlOperationOptions *options,
+ gboolean prepend_union)
+{
+ GrlTypeFilter filter = grl_operation_options_get_type_filter (options);
+ GString *sparql_filter = g_string_new ("");
+
+ if (filter & GRL_TYPE_FILTER_AUDIO) {
+ if (prepend_union) {
+ sparql_filter = g_string_append (sparql_filter,
+ "UNION { ?urn a nfo:Audio } ");
+ } else {
+ sparql_filter = g_string_append (sparql_filter,
+ "{ ?urn a nfo:Audio } ");
+ prepend_union = TRUE;
+ }
+ }
+ if (filter & GRL_TYPE_FILTER_VIDEO) {
+ if (prepend_union) {
+ sparql_filter = g_string_append (sparql_filter,
+ "UNION { ?urn a nmm:Video } ");
+ } else {
+ sparql_filter = g_string_append (sparql_filter,
+ "{ ?urn a nmm:Video } ");
+ prepend_union = TRUE;
+ }
+ }
+ if (filter & GRL_TYPE_FILTER_IMAGE) {
+ if (prepend_union) {
+ sparql_filter = g_string_append (sparql_filter,
+ "UNION { ?urn a nmm:Photo } ");
+ } else {
+ sparql_filter = g_string_append (sparql_filter,
+ "{ ?urn a nmm:Photo } ");
+ }
+ }
+
+ sparql_filter = g_string_append_c (sparql_filter, '.');
+
+ return g_string_free (sparql_filter, FALSE);
+}
+
+/* I can haz templatze ?? */
+#define TRACKER_QUERY_CB(spec_type,name,error) \
+ \
+ static void \
+ tracker_##name##_result_cb (GObject *source_object, \
+ GAsyncResult *result, \
+ GrlTrackerOp *os) \
+ { \
+ gint col; \
+ const gchar *sparql_type; \
+ GError *tracker_error = NULL, *error = NULL; \
+ GrlMedia *media; \
+ spec_type *spec = \
+ (spec_type *) os->data; \
+ \
+ GRL_ODEBUG ("%s", __FUNCTION__); \
+ \
+ if (g_cancellable_is_cancelled (os->cancel)) { \
+ GRL_ODEBUG ("\tOperation %u cancelled", spec->operation_id); \
+ spec->callback (spec->source, \
+ spec->operation_id, \
+ NULL, 0, \
+ spec->user_data, NULL); \
+ grl_tracker_queue_done (grl_tracker_queue, os); \
+ \
+ return; \
+ } \
+ \
+ if (!tracker_sparql_cursor_next_finish (os->cursor, \
+ result, \
+ &tracker_error)) { \
+ if (tracker_error != NULL) { \
+ GRL_WARNING ("\terror in parsing query id=%u : %s", \
+ spec->operation_id, tracker_error->message); \
+ \
+ error = g_error_new (GRL_CORE_ERROR, \
+ GRL_CORE_ERROR_##error##_FAILED, \
+ _("Failed to query: %s"), \
+ tracker_error->message); \
+ \
+ spec->callback (spec->source, \
+ spec->operation_id, \
+ NULL, 0, \
+ spec->user_data, error); \
+ \
+ g_error_free (error); \
+ g_error_free (tracker_error); \
+ } else { \
+ GRL_ODEBUG ("\tend of parsing id=%u :)", spec->operation_id); \
+ \
+ /* Only emit this last one if more result than expected */ \
+ if (os->count > 1) \
+ spec->callback (spec->source, \
+ spec->operation_id, \
+ NULL, 0, \
+ spec->user_data, NULL); \
+ } \
+ \
+ grl_tracker_queue_done (grl_tracker_queue, os); \
+ return; \
+ } \
+ \
+ sparql_type = tracker_sparql_cursor_get_string (os->cursor, \
+ 0, \
+ NULL); \
+ \
+ GRL_ODEBUG ("\tParsing line %i of type %s", \
+ os->current, sparql_type); \
+ \
+ media = grl_tracker_build_grilo_media (sparql_type, os->type_filter);\
+ \
+ if (media != NULL) { \
+ for (col = 1 ; \
+ col < tracker_sparql_cursor_get_n_columns (os->cursor) ; \
+ col++) { \
+ fill_grilo_media_from_sparql (GRL_TRACKER_SOURCE (spec->source), \
+ media, os->cursor, col); \
+ } \
+ set_title_from_filename (media); \
+ \
+ spec->callback (spec->source, \
+ spec->operation_id, \
+ media, \
+ --os->count, \
+ spec->user_data, \
+ NULL); \
+ } \
+ \
+ /* Schedule the next line to parse */ \
+ os->current++; \
+ if (os->count < 1) \
+ grl_tracker_queue_done (grl_tracker_queue, os); \
+ else \
+ tracker_sparql_cursor_next_async (os->cursor, os->cancel, \
+ (GAsyncReadyCallback) tracker_##name##_result_cb, \
+ (gpointer) os); \
+ } \
+ \
+ static void \
+ tracker_##name##_cb (GObject *source_object, \
+ GAsyncResult *result, \
+ GrlTrackerOp *os) \
+ { \
+ GError *tracker_error = NULL, *error = NULL; \
+ spec_type *spec = (spec_type *) os->data; \
+ TrackerSparqlConnection *connection = \
+ grl_tracker_source_get_tracker_connection (GRL_TRACKER_SOURCE (spec->source)); \
+ \
+ GRL_ODEBUG ("%s", __FUNCTION__); \
+ \
+ os->cursor = \
+ tracker_sparql_connection_query_finish (connection, \
+ result, &tracker_error); \
+ \
+ if (tracker_error) { \
+ GRL_WARNING ("Could not execute sparql query id=%u: %s", \
+ spec->operation_id, tracker_error->message); \
+ \
+ error = g_error_new (GRL_CORE_ERROR, \
+ GRL_CORE_ERROR_##error##_FAILED, \
+ _("Failed to query: %s"), \
+ tracker_error->message); \
+ \
+ spec->callback (spec->source, spec->operation_id, NULL, 0, \
+ spec->user_data, error); \
+ \
+ g_error_free (tracker_error); \
+ g_error_free (error); \
+ grl_tracker_queue_done (grl_tracker_queue, os); \
+ \
+ return; \
+ } \
+ \
+ /* Start parsing results */ \
+ os->current = 0; \
+ tracker_sparql_cursor_next_async (os->cursor, NULL, \
+ (GAsyncReadyCallback) tracker_##name##_result_cb, \
+ (gpointer) os); \
+ }
+
+TRACKER_QUERY_CB(GrlSourceQuerySpec, query, QUERY)
+TRACKER_QUERY_CB(GrlSourceBrowseSpec, browse, BROWSE)
+TRACKER_QUERY_CB(GrlSourceSearchSpec, search, SEARCH)
+
+static void
+tracker_resolve_cb (GObject *source_object,
+ GAsyncResult *result,
+ GrlTrackerOp *os)
+{
+ GrlSourceResolveSpec *rs = (GrlSourceResolveSpec *) os->data;
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (rs->source);
+ gint col;
+ GError *tracker_error = NULL, *error = NULL;
+ TrackerSparqlCursor *cursor;
+
+ GRL_ODEBUG ("%s", __FUNCTION__);
+
+ cursor = tracker_sparql_connection_query_finish (priv->tracker_connection,
+ result, &tracker_error);
+
+ if (tracker_error) {
+ GRL_WARNING ("Could not execute sparql resolve query : %s",
+ tracker_error->message);
+
+ error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_RESOLVE_FAILED,
+ _("Failed to resolve: %s"),
+ tracker_error->message);
+
+ rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, error);
+
+ g_error_free (tracker_error);
+ g_error_free (error);
+
+ goto end_operation;
+ }
+
+
+ if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
+ /* Translate Sparql result into Grilo result */
+ for (col = 0 ; col < tracker_sparql_cursor_get_n_columns (cursor) ; col++) {
+ fill_grilo_media_from_sparql (GRL_TRACKER_SOURCE (rs->source),
+ rs->media, cursor, col);
+ }
+ set_title_from_filename (rs->media);
+
+ rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, NULL);
+ } else {
+ rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, NULL);
+ }
+
+ end_operation:
+ g_clear_object (&cursor);
+
+ grl_tracker_queue_done (grl_tracker_queue, os);
+}
+
+static void
+tracker_media_from_uri_cb (GObject *source_object,
+ GAsyncResult *result,
+ GrlTrackerOp *os)
+{
+ GrlSourceMediaFromUriSpec *mfus = (GrlSourceMediaFromUriSpec *) os->data;
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (mfus->source);
+ GError *tracker_error = NULL, *error = NULL;
+ GrlMedia *media;
+ TrackerSparqlCursor *cursor;
+ const gchar *sparql_type;
+ gint col;
+
+ GRL_ODEBUG ("%s", __FUNCTION__);
+
+ cursor = tracker_sparql_connection_query_finish (priv->tracker_connection,
+ result, &tracker_error);
+
+ if (tracker_error) {
+ GRL_WARNING ("Could not execute sparql media from uri query : %s",
+ tracker_error->message);
+
+ error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_MEDIA_FROM_URI_FAILED,
+ _("Failed to get media from uri: %s"),
+ tracker_error->message);
+
+ mfus->callback (mfus->source, mfus->operation_id, NULL, mfus->user_data, error);
+
+ g_error_free (tracker_error);
+ g_error_free (error);
+
+ goto end_operation;
+ }
+
+
+ if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
+ /* Build grilo media */
+ sparql_type = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+ media = grl_tracker_build_grilo_media (sparql_type, GRL_TYPE_FILTER_NONE);
+
+ /* Translate Sparql result into Grilo result */
+ for (col = 0 ; col < tracker_sparql_cursor_get_n_columns (cursor) ; col++) {
+ fill_grilo_media_from_sparql (GRL_TRACKER_SOURCE (mfus->source),
+ media, cursor, col);
+ }
+ set_title_from_filename (media);
+
+ mfus->callback (mfus->source, mfus->operation_id, media, mfus->user_data, NULL);
+ } else {
+ mfus->callback (mfus->source, mfus->operation_id, NULL, mfus->user_data, NULL);
+ }
+
+ end_operation:
+ g_clear_object (&cursor);
+
+ grl_tracker_queue_done (grl_tracker_queue, os);
+}
+
+static void
+tracker_store_metadata_cb (GObject *source_object,
+ GAsyncResult *result,
+ GrlTrackerOp *os)
+{
+ GrlSourceStoreMetadataSpec *sms =
+ (GrlSourceStoreMetadataSpec *) os->data;
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (sms->source);
+ GError *tracker_error = NULL, *error = NULL;
+
+ tracker_sparql_connection_update_finish (priv->tracker_connection,
+ result,
+ &tracker_error);
+
+ if (tracker_error) {
+ GRL_WARNING ("Could not execute sparql update : %s",
+ tracker_error->message);
+
+ error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_STORE_METADATA_FAILED,
+ _("Failed to update metadata: %s"),
+ tracker_error->message);
+
+ sms->callback (sms->source, sms->media, NULL, sms->user_data, error);
+
+ g_error_free (tracker_error);
+ g_error_free (error);
+ } else {
+ sms->callback (sms->source, sms->media, NULL, sms->user_data, error);
+ }
+
+ grl_tracker_queue_done (grl_tracker_queue, os);
+}
+
+/**/
+
+const GList *
+grl_tracker_source_writable_keys (GrlSource *source)
+{
+ static GList *keys = NULL;
+ GrlRegistry *registry;
+ GrlKeyID grl_metadata_key_chromaprint;
+
+ if (!keys) {
+ registry = grl_registry_get_default ();
+ grl_metadata_key_chromaprint = grl_registry_lookup_metadata_key (registry, "chromaprint");
+
+ keys = grl_metadata_key_list_new (GRL_METADATA_KEY_PLAY_COUNT,
+ GRL_METADATA_KEY_LAST_PLAYED,
+ GRL_METADATA_KEY_LAST_POSITION,
+ GRL_METADATA_KEY_FAVOURITE,
+ GRL_METADATA_KEY_TITLE,
+ GRL_METADATA_KEY_TRACK_NUMBER,
+ GRL_METADATA_KEY_CREATION_DATE,
+ grl_metadata_key_chromaprint,
+ NULL);
+ }
+ return keys;
+}
+
+static void
+grl_tracker_source_get_duration_min_max (GrlOperationOptions *options,
+ int *min,
+ int *max)
+{
+ GValue *min_val, *max_val;
+
+ grl_operation_options_get_key_range_filter (options, GRL_METADATA_KEY_DURATION,
+ &min_val, &max_val);
+ if (min_val)
+ *min = g_value_get_int (min_val);
+ else
+ *min = -1;
+ if (max_val)
+ *max = g_value_get_int (max_val);
+ else
+ *max = -1;
+}
+
+static char *
+grl_tracker_source_create_constraint (int min, int max)
+{
+ if (min <= 0 && max <= 0)
+ return g_strdup ("");
+ if (max <= 0) {
+ return g_strdup_printf ("?urn a nfo:FileDataObject . "
+ "OPTIONAL {"
+ " ?urn nfo:duration ?duration "
+ "} . "
+ "FILTER(?duration > %d || !BOUND(?duration))",
+ min);
+ }
+ if (min <= 0) {
+ return g_strdup_printf ("?urn a nfo:FileDataObject . "
+ "OPTIONAL {"
+ " ?urn nfo:duration ?duration "
+ "} . "
+ "FILTER(?duration < %d || !BOUND(?duration))",
+ max);
+ }
+ return g_strdup_printf ("?urn a nfo:FileDataObject . "
+ "OPTIONAL {"
+ " ?urn nfo:duration ?duration "
+ "} . "
+ "FILTER(?duration < %d || ?duration > %d || !BOUND(?duration))",
+ max, min);
+}
+
+/**
+ * Query is a SPARQL query.
+ *
+ * Columns must be named with the Grilo key name that the column
+ * represent. Unnamed or unknown columns will be ignored.
+ *
+ * First column must be the media type, and it does not need to be named. It
+ * must match with any value supported in rdf:type() property, or
+ * grilo#Container. Types understood are:
+ *
+ * <itemizedlist>
+ * <listitem>
+ * <para>
+ * <literal>nmm#MusicPiece</literal>
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>
+ * <literal>nmm#Video</literal>
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>
+ * <literal>nmm#Photo</literal>
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>
+ * <literal>nmm#Artist</literal>
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>
+ * <literal>nmm#MusicAlbum</literal>
+ * </para>
+ * </listitem>
+ * <listitem>
+ * <para>
+ * <literal>grilo#Container</literal>
+ * </para>
+ * </listitem>
+ * </itemizedlist>
+ *
+ * An example for searching all songs:
+ *
+ * <informalexample>
+ * <programlisting>
+ * SELECT rdf:type(?song)
+ * ?song AS ?id
+ * nie:title(?song) AS ?title
+ * nie:url(?song) AS ?url
+ * WHERE { ?song a nmm:MusicPiece }
+ * </programlisting>
+ * </informalexample>
+ *
+ * Alternatively, we can use a partial SPARQL query: just specify the sentence
+ * in the WHERE part. In this case, "?urn" is the ontology concept to be used in
+ * the clause.
+ *
+ * An example of such partial query:
+ *
+ * <informalexample>
+ * <programlisting>
+ * ?urn a nfo:Media
+ * </programlisting>
+ * </informalexample>
+ *
+ * In this case, all data required to build a full SPARQL query will be get from
+ * the query spec.
+ */
+void
+grl_tracker_source_query (GrlSource *source,
+ GrlSourceQuerySpec *qs)
+{
+ GError *error = NULL;
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ gchar *constraint;
+ gchar *sparql_final;
+ gchar *sparql_select;
+ GrlTrackerOp *os;
+ gint count = grl_operation_options_get_count (qs->options);
+ guint skip = grl_operation_options_get_skip (qs->options);
+
+ GRL_IDEBUG ("%s: id=%u", __FUNCTION__, qs->operation_id);
+
+ if (!qs->query || qs->query[0] == '\0') {
+ error = g_error_new_literal (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_QUERY_FAILED,
+ _("Empty query"));
+ goto send_error;
+ }
+
+ /* Check if it is a full sparql query */
+ if (g_ascii_strncasecmp (qs->query, "select ", 7) != 0) {
+ constraint = grl_tracker_source_get_device_constraint (priv);
+ sparql_select = grl_tracker_source_get_select_string (qs->keys);
+ sparql_final = g_strdup_printf (TRACKER_QUERY_PARTIAL_REQUEST,
+ sparql_select,
+ qs->query,
+ constraint,
+ skip,
+ count);
+ g_free (constraint);
+ g_free (qs->query);
+ g_free (sparql_select);
+ qs->query = sparql_final;
+ } else {
+ /* Append offset and limit */
+ sparql_final = g_strdup_printf (TRACKER_QUERY_FULL_REQUEST,
+ qs->query,
+ skip,
+ count);
+ g_free (qs->query);
+ qs->query = sparql_final;
+ }
+
+ os = grl_tracker_op_initiate_query (qs->operation_id,
+ g_strdup (qs->query),
+ (GAsyncReadyCallback) tracker_query_cb,
+ qs);
+
+ os->keys = qs->keys;
+ os->skip = skip;
+ os->count = count;
+ os->type_filter = grl_operation_options_get_type_filter (qs->options);
+ os->data = qs;
+ /* os->cb.sr = qs->callback; */
+ /* os->user_data = qs->user_data; */
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ return;
+
+ send_error:
+ qs->callback (qs->source, qs->operation_id, NULL, 0, qs->user_data, error);
+ g_error_free (error);
+}
+
+void
+grl_tracker_source_resolve (GrlSource *source,
+ GrlSourceResolveSpec *rs)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ gchar *constraint = NULL, *sparql_select, *sparql_final;
+ gchar *sparql_type_filter = NULL;
+ const gchar *url = grl_media_get_url (rs->media);
+ GrlTrackerOp *os;
+
+ GRL_IDEBUG ("%s: id=%i", __FUNCTION__, rs->operation_id);
+
+ /* Check if the media comes from this source or another */
+ if (g_strcmp0 (priv->tracker_datasource, grl_source_get_id (rs->source)) == 0) {
+ if (grl_media_get_id (rs->media) == NULL) {
+ rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, NULL);
+ return;
+ } else {
+ sparql_select = grl_tracker_source_get_select_string (rs->keys);
+ sparql_final = g_strdup_printf (TRACKER_RESOLVE_REQUEST, sparql_select,
+ grl_media_get_id (rs->media));
+ }
+ } else {
+ if (url) {
+ sparql_select = grl_tracker_source_get_select_string (rs->keys);
+ sparql_final = g_strdup_printf (TRACKER_RESOLVE_URL_REQUEST,
+ sparql_select, url);
+ } else {
+ rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, NULL);
+ return;
+ }
+ }
+
+ GRL_IDEBUG ("\request: '%s'", sparql_final);
+
+ os = grl_tracker_op_initiate_metadata (sparql_final,
+ (GAsyncReadyCallback) tracker_resolve_cb,
+ rs);
+ os->keys = rs->keys;
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ g_clear_pointer (&sparql_type_filter, g_free);
+ g_clear_pointer (&constraint, g_free);
+ g_clear_pointer (&sparql_select, g_free);
+}
+
+gboolean
+grl_tracker_source_may_resolve (GrlSource *source,
+ GrlMedia *media,
+ GrlKeyID key_id,
+ GList **missing_keys)
+{
+ GRL_IDEBUG ("%s: key=%s", __FUNCTION__, GRL_METADATA_KEY_GET_NAME (key_id));
+
+ if (media && grl_tracker_source_find_source (grl_media_get_source (media))) {
+ return TRUE;
+ }
+
+ if (!grl_tracker_key_is_supported (key_id)) {
+ return FALSE;
+ }
+
+ if (media) {
+ if (grl_media_get_url (media)) {
+ return TRUE;
+ } else {
+ if (missing_keys) {
+ *missing_keys = g_list_append (*missing_keys,
+ GRLKEYID_TO_POINTER (GRL_METADATA_KEY_URL));
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+void
+grl_tracker_source_store_metadata (GrlSource *source,
+ GrlSourceStoreMetadataSpec *sms)
+{
+ gchar *sparql_delete, *sparql_cdelete, *sparql_insert, *sparql_final;
+ const gchar *urn = grl_data_get_string (GRL_DATA (sms->media),
+ grl_metadata_key_tracker_urn);
+ GrlTrackerOp *os;
+
+ GRL_IDEBUG ("%s: urn=%s", G_STRFUNC, urn);
+
+ sparql_delete = grl_tracker_get_delete_string (sms->keys);
+ sparql_cdelete = grl_tracker_get_delete_conditional_string (urn, sms->keys);
+ sparql_insert = grl_tracker_tracker_get_insert_string (sms->media, sms->keys);
+
+ if (g_strcmp0 (sparql_insert, "") == 0) {
+ sparql_final = g_strdup_printf (TRACKER_DELETE_REQUEST,
+ urn, sparql_delete,
+ urn, sparql_cdelete);
+ } else {
+ sparql_final = g_strdup_printf (TRACKER_SAVE_REQUEST,
+ urn, sparql_delete,
+ urn, sparql_cdelete,
+ urn, sparql_insert);
+ }
+
+ os = grl_tracker_op_initiate_set_metadata (sparql_final,
+ (GAsyncReadyCallback) tracker_store_metadata_cb,
+ sms);
+ os->keys = sms->keys;
+
+ GRL_IDEBUG ("\trequest: '%s'", sparql_final);
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ g_free (sparql_delete);
+ g_free (sparql_cdelete);
+ g_free (sparql_insert);
+}
+
+void
+grl_tracker_source_search (GrlSource *source, GrlSourceSearchSpec *ss)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ gchar *constraint;
+ gchar *sparql_select;
+ gchar *sparql_final;
+ gchar *sparql_type_filter;
+ gchar *escaped_text;
+ GrlTrackerOp *os;
+ gint count = grl_operation_options_get_count (ss->options);
+ guint skip = grl_operation_options_get_skip (ss->options);
+ int min_dur, max_dur;
+ char *duration_constraint;
+ GRL_IDEBUG ("%s: id=%u", __FUNCTION__, ss->operation_id);
+
+ constraint = grl_tracker_source_get_device_constraint (priv);
+ sparql_select = grl_tracker_source_get_select_string (ss->keys);
+ sparql_type_filter = get_sparql_type_filter (ss->options, FALSE);
+ grl_tracker_source_get_duration_min_max (ss->options, &min_dur, &max_dur);
+ duration_constraint = grl_tracker_source_create_constraint (min_dur, max_dur);
+ if (!ss->text || ss->text[0] == '\0') {
+ /* Search all */
+ sparql_final = g_strdup_printf (TRACKER_SEARCH_ALL_REQUEST, sparql_select,
+ constraint, duration_constraint, sparql_type_filter,
+ skip, count);
+ } else {
+ escaped_text = tracker_sparql_escape_string (ss->text);
+ sparql_final = g_strdup_printf (TRACKER_SEARCH_REQUEST, sparql_select,
+ sparql_type_filter, escaped_text,
+ constraint, duration_constraint, skip, count);
+ g_free (escaped_text);
+ }
+
+ GRL_IDEBUG ("\tselect: '%s'", sparql_final);
+
+ os = grl_tracker_op_initiate_query (ss->operation_id,
+ sparql_final,
+ (GAsyncReadyCallback) tracker_search_cb,
+ ss);
+ os->keys = ss->keys;
+ os->skip = skip;
+ os->count = count;
+ os->type_filter = grl_operation_options_get_type_filter (ss->options);
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ g_free (constraint);
+ g_free (sparql_select);
+ g_free (sparql_type_filter);
+ g_free (duration_constraint);
+}
+
+static gboolean
+is_root_box (GrlMedia *container)
+{
+ if (container == NULL)
+ return TRUE;
+ if (!grl_media_get_id (container))
+ return TRUE;
+ return FALSE;
+}
+
+static void
+grl_tracker_source_browse_category (GrlSource *source,
+ GrlSourceBrowseSpec *bs)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ gchar *constraint;
+ gchar *sparql_select;
+ gchar *sparql_final;
+ GrlTrackerOp *os;
+ GrlMedia *media;
+ const gchar *category;
+ gint remaining;
+ gint count = grl_operation_options_get_count (bs->options);
+ guint skip = grl_operation_options_get_skip (bs->options);
+ GrlTypeFilter filter = grl_operation_options_get_type_filter (bs->options);
+ int min_dur, max_dur;
+ char *duration_constraint;
+
+ GRL_IDEBUG ("%s: id=%u", __FUNCTION__, bs->operation_id);
+
+ /* If the category is missing, try to get it from the
+ * container's ID */
+ if (!is_root_box (bs->container) &&
+ !grl_data_has_key (GRL_DATA (bs->container),
+ grl_metadata_key_tracker_category)) {
+ const char *id;
+
+ id = grl_media_get_id (bs->container);
+ if (g_strcmp0 (id, "documents") == 0)
+ category = "nfo:Document";
+ else if (g_strcmp0 (id, "music") == 0)
+ category = "nmm:MusicPiece";
+ else if (g_strcmp0 (id, "photos") == 0)
+ category = "nmm:Photo";
+ else if (g_strcmp0 (id, "videos") == 0)
+ category = "nmm:Video";
+ else {
+ GError *error;
+
+ error = g_error_new (GRL_CORE_ERROR,
+ GRL_CORE_ERROR_BROWSE_FAILED,
+ _("ID “%s” is not known in this source"),
+ id);
+
+ bs->callback (bs->source, bs->operation_id, NULL, 0,
+ bs->user_data, error);
+
+ g_error_free (error);
+ return;
+ }
+
+ grl_data_set_string (GRL_DATA (bs->container),
+ grl_metadata_key_tracker_category,
+ category);
+ }
+
+ if (is_root_box (bs->container) ||
+ !grl_data_has_key (GRL_DATA (bs->container),
+ grl_metadata_key_tracker_category)) {
+ /* Hardcoded categories */
+ if (filter == GRL_TYPE_FILTER_ALL) {
+ remaining = 3;
+ if (grl_tracker_show_documents) {
+ remaining++;
+ }
+ } else {
+ remaining = 0;
+ if (filter & GRL_TYPE_FILTER_AUDIO) {
+ remaining++;
+ }
+ if (filter & GRL_TYPE_FILTER_VIDEO) {
+ remaining++;
+ }
+ if (filter & GRL_TYPE_FILTER_IMAGE) {
+ remaining++;
+ }
+ }
+
+ if (remaining == 0) {
+ bs->callback (bs->source, bs->operation_id, NULL, 0,
+ bs->user_data, NULL);
+ return;
+ }
+
+ /* Special case: if everthing is filtered except one category, then skip the
+ intermediate level and go straightly to the elements */
+ if (remaining == 1) {
+ if (filter & GRL_TYPE_FILTER_AUDIO) {
+ category = "nmm:MusicPiece";
+ } else if (filter & GRL_TYPE_FILTER_IMAGE) {
+ category = "nmm:Photo";
+ } else {
+ category = "nmm:Video";
+ }
+ } else {
+ if (remaining == 4) {
+ media = grl_media_container_new ();
+ grl_media_set_title (media, "Documents");
+ grl_media_set_id (media, "documents");
+ grl_data_set_string (GRL_DATA (media),
+ grl_metadata_key_tracker_category,
+ "nfo:Document");
+ bs->callback (bs->source, bs->operation_id, media, --remaining,
+ bs->user_data, NULL);
+ }
+
+ if (filter & GRL_TYPE_FILTER_AUDIO) {
+ media = grl_media_container_new ();
+ grl_media_set_title (media, "Music");
+ grl_media_set_id (media, "music");
+ grl_data_set_string (GRL_DATA (media),
+ grl_metadata_key_tracker_category,
+ "nmm:MusicPiece");
+ bs->callback (bs->source, bs->operation_id, media, --remaining,
+ bs->user_data, NULL);
+ }
+
+ if (filter & GRL_TYPE_FILTER_IMAGE) {
+ media = grl_media_container_new ();
+ grl_media_set_title (media, "Photos");
+ grl_media_set_id (media, "photos");
+ grl_data_set_string (GRL_DATA (media),
+ grl_metadata_key_tracker_category,
+ "nmm:Photo");
+ bs->callback (bs->source, bs->operation_id, media, --remaining,
+ bs->user_data, NULL);
+ }
+
+ if (filter & GRL_TYPE_FILTER_VIDEO) {
+ media = grl_media_container_new ();
+ grl_media_set_title (media, "Videos");
+ grl_media_set_id (media, "videos");
+ grl_data_set_string (GRL_DATA (media),
+ grl_metadata_key_tracker_category,
+ "nmm:Video");
+ bs->callback (bs->source, bs->operation_id, media, --remaining,
+ bs->user_data, NULL);
+ }
+ return;
+ }
+ } else {
+ category = grl_data_get_string (GRL_DATA (bs->container),
+ grl_metadata_key_tracker_category);
+ }
+
+ grl_tracker_source_get_duration_min_max (bs->options, &min_dur, &max_dur);
+ duration_constraint = grl_tracker_source_create_constraint (min_dur, max_dur);
+ constraint = grl_tracker_source_get_device_constraint (priv);
+ sparql_select = grl_tracker_source_get_select_string (bs->keys);
+ sparql_final = g_strdup_printf (TRACKER_BROWSE_CATEGORY_REQUEST,
+ sparql_select,
+ category,
+ constraint,
+ duration_constraint,
+ skip, count);
+
+ GRL_IDEBUG ("\tselect: '%s'", sparql_final);
+
+ os = grl_tracker_op_initiate_query (bs->operation_id,
+ sparql_final,
+ (GAsyncReadyCallback) tracker_browse_cb,
+ bs);
+ os->keys = bs->keys;
+ os->skip = skip;
+ os->count = count;
+ os->type_filter = grl_operation_options_get_type_filter (bs->options);
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ g_free (constraint);
+ g_free (sparql_select);
+ g_free (duration_constraint);
+}
+
+static void
+grl_tracker_source_browse_filesystem (GrlSource *source,
+ GrlSourceBrowseSpec *bs)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ gchar *constraint;
+ gchar *sparql_select;
+ gchar *sparql_final;
+ gchar *sparql_type_filter;
+ GrlTrackerOp *os;
+ gint count = grl_operation_options_get_count (bs->options);
+ guint skip = grl_operation_options_get_skip (bs->options);
+ int min_dur, max_dur;
+ char *duration_constraint;
+
+ GRL_IDEBUG ("%s: id=%u", __FUNCTION__, bs->operation_id);
+
+ sparql_select = grl_tracker_source_get_select_string (bs->keys);
+ constraint = grl_tracker_source_get_device_constraint (priv);
+ sparql_type_filter = get_sparql_type_filter (bs->options, TRUE);
+ grl_tracker_source_get_duration_min_max (bs->options, &min_dur, &max_dur);
+ duration_constraint = grl_tracker_source_create_constraint (min_dur, max_dur);
+
+ if (bs->container == NULL ||
+ !grl_media_get_id (bs->container)) {
+ sparql_final = g_strdup_printf (TRACKER_BROWSE_FILESYSTEM_ROOT_REQUEST,
+ sparql_select,
+ grl_tracker_show_documents? TRACKER_BROWSE_SHOW_DOCUMENTS: "",
+ sparql_type_filter,
+ constraint,
+ skip, count);
+
+ } else {
+ sparql_final = g_strdup_printf (TRACKER_BROWSE_FILESYSTEM_REQUEST,
+ sparql_select,
+ grl_tracker_show_documents? TRACKER_BROWSE_SHOW_DOCUMENTS: "",
+ sparql_type_filter,
+ constraint,
+ grl_media_get_id (bs->container),
+ skip, count);
+ }
+
+ GRL_IDEBUG ("\tselect: '%s'", sparql_final);
+
+ os = grl_tracker_op_initiate_query (bs->operation_id,
+ sparql_final,
+ (GAsyncReadyCallback) tracker_browse_cb,
+ bs);
+ os->keys = bs->keys;
+ os->skip = skip;
+ os->count = count;
+ os->type_filter = grl_operation_options_get_type_filter (bs->options);
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ g_free (sparql_type_filter);
+ g_free (constraint);
+ g_free (sparql_select);
+ g_free (duration_constraint);
+}
+
+void
+grl_tracker_source_browse (GrlSource *source,
+ GrlSourceBrowseSpec *bs)
+{
+ /* Ensure GRL_METADATA_KEY_ID is always requested */
+ if (!g_list_find (bs->keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ID)))
+ bs->keys = g_list_prepend (bs->keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ID));
+
+ if (grl_tracker_browse_filesystem)
+ grl_tracker_source_browse_filesystem (source, bs);
+ else
+ grl_tracker_source_browse_category (source, bs);
+}
+
+void
+grl_tracker_source_cancel (GrlSource *source, guint operation_id)
+{
+ GrlTrackerOp *os;
+
+ GRL_IDEBUG ("%s: id=%u", __FUNCTION__, operation_id);
+
+ os = g_hash_table_lookup (grl_tracker_operations,
+ GSIZE_TO_POINTER (operation_id));
+
+ if (os != NULL)
+ grl_tracker_queue_cancel (grl_tracker_queue, os);
+}
+
+gboolean
+grl_tracker_source_change_start (GrlSource *source, GError **error)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+ priv->notify_changes = TRUE;
+
+ return TRUE;
+}
+
+gboolean
+grl_tracker_source_change_stop (GrlSource *source, GError **error)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+ priv->notify_changes = FALSE;
+
+ return TRUE;
+}
+
+void
+grl_tracker_source_init_requests (void)
+{
+ GrlRegistry *registry = grl_registry_get_default ();
+
+ grl_metadata_key_tracker_category =
+ grl_registry_lookup_metadata_key (registry, "tracker-category");
+
+ grl_tracker_operations = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ GRL_LOG_DOMAIN_INIT (tracker_source_request_log_domain,
+ "tracker-source-request");
+ GRL_LOG_DOMAIN_INIT (tracker_source_result_log_domain,
+ "tracker-source-result");
+}
+
+GrlCaps *
+grl_tracker_source_get_caps (GrlSource *source,
+ GrlSupportedOps operation)
+{
+ static GrlCaps *caps;
+
+ if (!caps) {
+ GList *range_list;
+ caps = grl_caps_new ();
+ grl_caps_set_type_filter (caps, GRL_TYPE_FILTER_ALL);
+ range_list = grl_metadata_key_list_new (GRL_METADATA_KEY_DURATION,
+ GRL_METADATA_KEY_INVALID);
+ grl_caps_set_key_range_filter (caps, range_list);
+ g_list_free (range_list);
+ }
+
+ return caps;
+}
+
+GrlSupportedOps
+grl_tracker_source_supported_operations (GrlSource *source)
+{
+ gboolean is_extractor;
+ GrlSupportedOps ops;
+
+ /* Always supported operations. */
+ ops = GRL_OP_RESOLVE | GRL_OP_MEDIA_FROM_URI | GRL_OP_SEARCH | GRL_OP_QUERY |
+ GRL_OP_STORE_METADATA | GRL_OP_NOTIFY_CHANGE;
+
+ /* The extractor doesn’t support browsing; only resolving. */
+ is_extractor = g_str_has_prefix (grl_source_get_id (source),
+ "http://www.tracker-project.org"
+ "/ontologies/tracker"
+ "#extractor-data-source,");
+ if (!is_extractor) {
+ ops |= GRL_OP_BROWSE;
+ }
+
+ return ops;
+}
+
+gboolean
+grl_tracker_source_test_media_from_uri (GrlSource *source,
+ const gchar *uri)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ GError *error = NULL;
+ TrackerSparqlCursor *cursor;
+ gboolean empty;
+ gchar *constraint;
+ gchar *sparql_final;
+
+ constraint = grl_tracker_source_get_device_constraint (priv);
+ if (grl_tracker_show_documents) {
+ sparql_final = g_strdup_printf (TRACKER_TEST_MEDIA_FROM_URI_REQUEST_WITH_DOCUMENTS,
+ uri,
+ constraint);
+ } else {
+ sparql_final = g_strdup_printf (TRACKER_TEST_MEDIA_FROM_URI_REQUEST,
+ uri,
+ constraint);
+ }
+
+ cursor = tracker_sparql_connection_query (grl_tracker_connection,
+ sparql_final,
+ NULL,
+ &error);
+ g_free (constraint);
+ g_free (sparql_final);
+
+ if (error) {
+ GRL_WARNING ("Error when executig sparql query: %s",
+ error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /* Check if there are results */
+ if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
+ empty = FALSE;
+ } else {
+ empty = TRUE;
+ }
+
+ g_object_unref (cursor);
+
+ return !empty;
+}
+
+void
+grl_tracker_source_get_media_from_uri (GrlSource *source,
+ GrlSourceMediaFromUriSpec *mfus)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+ gchar *constraint;
+ gchar *sparql_select;
+ gchar *sparql_final;
+ GrlTrackerOp *os;
+
+ GRL_IDEBUG ("%s: id=%u", __FUNCTION__, mfus->operation_id);
+
+ /* Ensure GRL_METADATA_KEY_ID is always requested */
+ if (!g_list_find (mfus->keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ID)))
+ mfus->keys = g_list_prepend (mfus->keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ID));
+
+ constraint = grl_tracker_source_get_device_constraint (priv);
+ sparql_select = grl_tracker_source_get_select_string (mfus->keys);
+ sparql_final = g_strdup_printf (TRACKER_MEDIA_FROM_URI_REQUEST,
+ sparql_select,
+ mfus->uri,
+ constraint);
+
+ GRL_IDEBUG ("\tselect: '%s'", sparql_final);
+
+ os = grl_tracker_op_initiate_metadata (sparql_final,
+ (GAsyncReadyCallback) tracker_media_from_uri_cb,
+ mfus);
+ os->keys = mfus->keys;
+
+ grl_tracker_queue_push (grl_tracker_queue, os);
+
+ g_free (constraint);
+ g_free (sparql_select);
+}
diff --git a/src/tracker3/grl-tracker-source-api.h b/src/tracker3/grl-tracker-source-api.h
new file mode 100644
index 0000000..f452e75
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-api.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_SOURCE_API_H_
+#define _GRL_TRACKER_SOURCE_API_H_
+
+#include "grl-tracker-source.h"
+
+/**/
+
+void grl_tracker_source_init_requests (void);
+
+const GList *grl_tracker_source_writable_keys (GrlSource *source);
+
+void grl_tracker_source_query (GrlSource *source,
+ GrlSourceQuerySpec *qs);
+
+void grl_tracker_source_resolve (GrlSource *source,
+ GrlSourceResolveSpec *rs);
+
+gboolean grl_tracker_source_may_resolve (GrlSource *source,
+ GrlMedia *media,
+ GrlKeyID key_id,
+ GList **missing_keys);
+
+void grl_tracker_source_store_metadata (GrlSource *source,
+ GrlSourceStoreMetadataSpec *sms);
+
+void grl_tracker_source_cancel (GrlSource *source, guint operation_id);
+
+void grl_tracker_source_search (GrlSource *source,
+ GrlSourceSearchSpec *ss);
+
+void grl_tracker_source_browse (GrlSource *source,
+ GrlSourceBrowseSpec *bs);
+
+gboolean grl_tracker_source_change_start (GrlSource *source,
+ GError **error);
+
+gboolean grl_tracker_source_change_stop (GrlSource *source,
+ GError **error);
+
+GrlCaps *grl_tracker_source_get_caps (GrlSource *source,
+ GrlSupportedOps operation);
+
+GrlSupportedOps grl_tracker_source_supported_operations (GrlSource *source);
+
+gboolean grl_tracker_source_test_media_from_uri (GrlSource *source,
+ const gchar *uri);
+
+void grl_tracker_source_get_media_from_uri (GrlSource *source,
+ GrlSourceMediaFromUriSpec *mfus);
+
+#endif /* _GRL_TRACKER_SOURCE_API_H_ */
diff --git a/src/tracker3/grl-tracker-source-cache.c b/src/tracker3/grl-tracker-source-cache.c
new file mode 100644
index 0000000..0ad04b2
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-cache.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <glib.h>
+
+#include "grl-tracker-source-cache.h"
+
+typedef struct {
+ GrlTrackerSource *source;
+
+ GHashTable *id_table;
+} GrlTrackerCacheSource;
+
+struct _GrlTrackerCache {
+ gsize size_limit;
+ gsize size_current;
+
+ GHashTable *id_table;
+ GHashTable *source_table;
+ GList *id_list;
+};
+
+static GrlTrackerCacheSource *
+grl_tracker_cache_source_new (GrlTrackerSource *source)
+{
+ GrlTrackerCacheSource *csource = g_slice_new0 (GrlTrackerCacheSource);
+
+ csource->source = source;
+ csource->id_table = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ return csource;
+}
+
+static void
+grl_tracker_cache_source_free (GrlTrackerCacheSource *csource)
+{
+ g_hash_table_destroy (csource->id_table);
+
+ g_slice_free (GrlTrackerCacheSource, csource);
+}
+
+/**/
+
+GrlTrackerCache *
+grl_tracker_source_cache_new (gsize size)
+{
+ GrlTrackerCache *cache;
+
+ g_return_val_if_fail (size > 0, NULL);
+
+ cache = g_slice_new0 (GrlTrackerCache);
+
+ if (!cache)
+ return NULL;
+
+ cache->size_limit = size;
+ cache->id_table = g_hash_table_new (g_direct_hash, g_direct_equal);
+ cache->source_table = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ return cache;
+}
+
+void
+grl_tracker_source_cache_free (GrlTrackerCache *cache)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (cache != NULL);
+
+ g_hash_table_iter_init (&iter, cache->source_table);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ grl_tracker_source_cache_del_source (cache, key);
+ }
+
+ if (cache->id_list) {
+ g_warning ("Memleak detected");
+ g_list_free (cache->id_list);
+ }
+ g_hash_table_destroy (cache->id_table);
+ g_hash_table_destroy (cache->source_table);
+
+ g_slice_free (GrlTrackerCache, cache);
+}
+
+void
+grl_tracker_source_cache_add_item (GrlTrackerCache *cache,
+ guint id,
+ GrlTrackerSource *source)
+{
+ GList *lid;
+ GrlTrackerCacheSource *csource;
+
+ g_return_if_fail (cache != NULL);
+
+ if (g_hash_table_lookup (cache->id_table, GSIZE_TO_POINTER (id)) != NULL)
+ return; /* TODO: is it worth to have an LRU ? */
+
+ csource = g_hash_table_lookup (cache->source_table, source);
+
+ if (!csource) {
+ csource = grl_tracker_cache_source_new (source);
+ g_hash_table_insert (cache->source_table, source, csource);
+ }
+
+ if (cache->size_current >= cache->size_limit) {
+ lid = g_list_last (cache->id_list); /* TODO: optimize that ! */
+ g_hash_table_remove (cache->id_table, lid->data);
+ cache->id_list = g_list_remove_link (cache->id_list, lid);
+
+ lid->data = GSIZE_TO_POINTER (id);
+ lid->next = cache->id_list;
+ cache->id_list->prev = lid;
+ cache->id_list = lid;
+ } else {
+ cache->id_list = g_list_prepend (cache->id_list, GSIZE_TO_POINTER (id));
+ cache->size_current++;
+ }
+
+ g_hash_table_insert (cache->id_table, GSIZE_TO_POINTER (id), csource);
+ g_hash_table_insert (csource->id_table, GSIZE_TO_POINTER (id),
+ cache->id_list);
+}
+
+void
+grl_tracker_source_cache_del_source (GrlTrackerCache *cache,
+ GrlTrackerSource *source)
+{
+ GrlTrackerCacheSource *csource;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (cache != NULL);
+ g_return_if_fail (source != NULL);
+
+ csource = g_hash_table_lookup (cache->source_table, source);
+
+ if (!csource)
+ return;
+
+ g_hash_table_iter_init (&iter, csource->id_table);
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_hash_table_remove (cache->id_table, key);
+ cache->id_list = g_list_delete_link (cache->id_list, value);
+ }
+
+ g_hash_table_remove (cache->source_table, source);
+ grl_tracker_cache_source_free (csource);
+}
+
+GrlTrackerSource *
+grl_tracker_source_cache_get_source (GrlTrackerCache *cache, guint id)
+{
+ GrlTrackerCacheSource *csource;
+
+ g_return_val_if_fail (cache != NULL, NULL);
+
+ csource = (GrlTrackerCacheSource *) g_hash_table_lookup (cache->id_table,
+ GSIZE_TO_POINTER (id));
+
+ if (csource) {
+ return csource->source;
+ }
+
+ return NULL;
+}
diff --git a/src/tracker3/grl-tracker-source-cache.h b/src/tracker3/grl-tracker-source-cache.h
new file mode 100644
index 0000000..a84930b
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-cache.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_SOURCE_CACHE_H_
+#define _GRL_TRACKER_SOURCE_CACHE_H_
+
+#include "grl-tracker-source.h"
+
+typedef struct _GrlTrackerCache GrlTrackerCache;
+
+GrlTrackerCache *grl_tracker_source_cache_new (gsize size);
+
+void grl_tracker_source_cache_free (GrlTrackerCache *cache);
+
+void grl_tracker_source_cache_add_item (GrlTrackerCache *cache,
+ guint id,
+ GrlTrackerSource *source);
+void grl_tracker_source_cache_del_source (GrlTrackerCache *cache,
+ GrlTrackerSource *source);
+
+GrlTrackerSource *grl_tracker_source_cache_get_source (GrlTrackerCache *cache,
+ guint id);
+
+#endif /* _GRL_TRACKER_SOURCE_CACHE_H_ */
diff --git a/src/tracker3/grl-tracker-source-notif.c b/src/tracker3/grl-tracker-source-notif.c
new file mode 100644
index 0000000..fa415be
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-notif.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2015 Collabora Ltd.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ * Xavier Claessens <xavier.claessens@collabora.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "grl-tracker.h"
+#include "grl-tracker-source-notif.h"
+#include "grl-tracker-source-priv.h"
+#include "grl-tracker-utils.h"
+
+#define GRL_LOG_DOMAIN_DEFAULT tracker_notif_log_domain
+GRL_LOG_DOMAIN_STATIC(tracker_notif_log_domain);
+
+#define GRL_TRACKER_TYPE_SOURCE_NOTIFY grl_tracker_source_notify_get_type ()
+G_DECLARE_FINAL_TYPE (GrlTrackerSourceNotify, grl_tracker_source_notify, GRL_TRACKER, SOURCE_NOTIFY, GObject)
+
+struct _GrlTrackerSourceNotify {
+ GObject parent;
+ TrackerSparqlConnection *connection;
+ TrackerNotifier *notifier;
+ guint events_signal_id;
+};
+
+enum {
+ PROP_0,
+ PROP_CONNECTION,
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { 0, };
+
+G_DEFINE_TYPE (GrlTrackerSourceNotify, grl_tracker_source_notify, G_TYPE_OBJECT)
+
+static GrlTrackerSourceNotify *singleton = NULL;
+
+static GrlMedia *
+media_for_event (GrlTrackerSourceNotify *self,
+ TrackerNotifierEvent *event)
+{
+ gchar *id_str;
+ GrlMedia *media;
+
+ id_str = g_strdup_printf ("%" G_GINT64_FORMAT, tracker_notifier_event_get_id (event));
+ // FIXME
+ media = grl_tracker_build_grilo_media (NULL,//tracker_notifier_event_get_type (event),
+ GRL_TYPE_FILTER_NONE);
+ grl_media_set_id (media, id_str);
+ grl_media_set_url (media, tracker_notifier_event_get_urn (event));
+
+ g_free (id_str);
+
+ return media;
+}
+
+static void
+handle_changes (GrlTrackerSourceNotify *self,
+ GPtrArray *events,
+ TrackerNotifierEventType tracker_type,
+ GrlSourceChangeType change_type)
+{
+ GrlTrackerSource *source = NULL;
+ TrackerNotifierEvent *event;
+ GPtrArray *change_list;
+ GrlMedia *media;
+ gint i;
+
+ source = grl_tracker_source_find ("");
+
+ if (!source || !grl_tracker_source_can_notify (source))
+ return;
+
+ change_list = g_ptr_array_new ();
+
+ for (i = 0; i < events->len; i++) {
+ event = g_ptr_array_index (events, i);
+ if (tracker_notifier_event_get_event_type (event) != tracker_type)
+ continue;
+
+ media = media_for_event (self, event);
+ g_ptr_array_add (change_list, media);
+ }
+
+ grl_source_notify_change_list (GRL_SOURCE (source), change_list,
+ change_type, FALSE);
+}
+
+static void
+notifier_event_cb (GrlTrackerSourceNotify *self,
+ const gchar *service,
+ const gchar *graph,
+ GPtrArray *events,
+ gpointer user_data)
+{
+ handle_changes (self, events,
+ TRACKER_NOTIFIER_EVENT_CREATE,
+ GRL_CONTENT_ADDED);
+ handle_changes (self, events,
+ TRACKER_NOTIFIER_EVENT_UPDATE,
+ GRL_CONTENT_CHANGED);
+ handle_changes (self, events,
+ TRACKER_NOTIFIER_EVENT_DELETE,
+ GRL_CONTENT_REMOVED);
+}
+
+static void
+grl_tracker_source_notify_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GrlTrackerSourceNotify *self = GRL_TRACKER_SOURCE_NOTIFY (object);
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->connection);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+grl_tracker_source_notify_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GrlTrackerSourceNotify *self = GRL_TRACKER_SOURCE_NOTIFY (object);
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ self->connection = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+grl_tracker_source_notify_constructed (GObject *object)
+{
+ GrlTrackerSourceNotify *self = GRL_TRACKER_SOURCE_NOTIFY (object);
+
+ self->notifier =
+ tracker_sparql_connection_create_notifier (self->connection);
+ self->events_signal_id =
+ g_signal_connect_swapped (self->notifier, "events",
+ G_CALLBACK (notifier_event_cb), object);
+
+ G_OBJECT_CLASS (grl_tracker_source_notify_parent_class)->constructed (object);
+}
+
+static void
+grl_tracker_source_notify_finalize (GObject *object)
+{
+ GrlTrackerSourceNotify *self = GRL_TRACKER_SOURCE_NOTIFY (object);
+
+ if (self->events_signal_id)
+ g_signal_handler_disconnect (self->notifier, self->events_signal_id);
+ g_clear_object (&self->notifier);
+ G_OBJECT_CLASS (grl_tracker_source_notify_parent_class)->finalize (object);
+}
+
+static void
+grl_tracker_source_notify_class_init (GrlTrackerSourceNotifyClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *) klass;
+
+ GRL_LOG_DOMAIN_INIT (tracker_notif_log_domain, "tracker-notif");
+ object_class->set_property = grl_tracker_source_notify_set_property;
+ object_class->get_property = grl_tracker_source_notify_get_property;
+ object_class->finalize = grl_tracker_source_notify_finalize;
+ object_class->constructed = grl_tracker_source_notify_constructed;
+
+ props[PROP_CONNECTION] =
+ g_param_spec_object ("connection",
+ "SPARQL Connection",
+ "SPARQL Connection",
+ TRACKER_TYPE_SPARQL_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+grl_tracker_source_notify_init (GrlTrackerSourceNotify *self)
+{
+}
+
+void
+grl_tracker_notify_init (TrackerSparqlConnection *sparql_conn)
+{
+ if (singleton != NULL)
+ return;
+
+ singleton = g_object_new (GRL_TRACKER_TYPE_SOURCE_NOTIFY,
+ "connection", sparql_conn,
+ NULL);
+}
+
+void
+grl_tracker_notify_shutdown (void)
+{
+ g_clear_object (&singleton);
+}
diff --git a/src/tracker3/grl-tracker-source-notif.h b/src/tracker3/grl-tracker-source-notif.h
new file mode 100644
index 0000000..dd78722
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-notif.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_SOURCE_NOTIF_H_
+#define _GRL_TRACKER_SOURCE_NOTIF_H_
+
+#include "grl-tracker-source.h"
+
+/* ------- Definitions ------- */
+
+#define TRACKER_DATASOURCES_REQUEST \
+ "SELECT " \
+ "(SELECT GROUP_CONCAT(rdf:type(?source), \":\") " \
+ " WHERE { ?urn nie:dataSource ?source }) " \
+ "nie:dataSource(?urn) " \
+ "(SELECT GROUP_CONCAT(nie:title(?source), \":\") " \
+ " WHERE { ?urn nie:dataSource ?source }) " \
+ "(SELECT GROUP_CONCAT(nie:url(tracker:mountPoint(?source)), \":\") " \
+ " WHERE { ?urn nie:dataSource ?source }) " \
+ "tracker:available(?urn) " \
+ "WHERE " \
+ "{ " \
+ "?urn a nfo:FileDataObject . FILTER (bound(nie:dataSource(?urn)))" \
+ "} " \
+ "GROUP BY (nie:dataSource(?urn))"
+
+/**/
+
+void grl_tracker_notify_init (TrackerSparqlConnection *sparql_conn);
+void grl_tracker_notify_shutdown (void);
+
+#endif /* _GRL_TRACKER_SOURCE_NOTIF_H_ */
diff --git a/src/tracker3/grl-tracker-source-priv.h b/src/tracker3/grl-tracker-source-priv.h
new file mode 100644
index 0000000..95dc5d7
--- /dev/null
+++ b/src/tracker3/grl-tracker-source-priv.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ * Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_SOURCE_PRIV_H_
+#define _GRL_TRACKER_SOURCE_PRIV_H_
+
+#include "grl-tracker-source.h"
+#include "grl-tracker-source-cache.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <tracker-sparql.h>
+
+/* ---- Source information ---- */
+
+#define GRL_TRACKER_SOURCE_ID "grl-tracker3-source"
+#define GRL_TRACKER_SOURCE_NAME "Tracker3"
+#define GRL_TRACKER_SOURCE_DESC _("A plugin for searching multimedia content using Tracker3")
+
+#define GRL_TRACKER_AUTHOR "Igalia S.L."
+#define GRL_TRACKER_LICENSE "LGPL"
+#define GRL_TRACKER_SITE "http://www.igalia.com"
+
+/**/
+
+#define GRL_TRACKER_SOURCE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GRL_TRACKER_SOURCE_TYPE, \
+ GrlTrackerSourcePriv))
+
+typedef enum {
+ GRL_TRACKER_SOURCE_STATE_INSERTING,
+ GRL_TRACKER_SOURCE_STATE_RUNNING,
+ GRL_TRACKER_SOURCE_STATE_DELETING,
+ GRL_TRACKER_SOURCE_STATE_DELETED,
+} GrlTrackerSourceState;
+
+struct _GrlTrackerSourcePriv {
+ TrackerSparqlConnection *tracker_connection;
+
+ GHashTable *operations;
+
+ gchar *tracker_datasource;
+ gboolean notify_changes;
+
+ GrlTrackerSourceState state;
+};
+
+/**/
+
+extern GrlPlugin *grl_tracker_plugin;
+
+/* shared data across */
+extern GrlTrackerCache *grl_tracker_item_cache;
+extern gboolean grl_tracker_upnp_present;
+
+/* tracker plugin config */
+extern gboolean grl_tracker_browse_filesystem;
+extern gboolean grl_tracker_show_documents;
+
+#endif /* _GRL_TRACKER_SOURCE_PRIV_H_ */
diff --git a/src/tracker3/grl-tracker-source.c b/src/tracker3/grl-tracker-source.c
new file mode 100644
index 0000000..78c3998
--- /dev/null
+++ b/src/tracker3/grl-tracker-source.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <grilo.h>
+#include <string.h>
+#include <tracker-sparql.h>
+
+#include "grl-tracker.h"
+#include "grl-tracker-source.h"
+#include "grl-tracker-source-priv.h"
+#include "grl-tracker-source-api.h"
+#include "grl-tracker-source-cache.h"
+#include "grl-tracker-source-notif.h"
+#include "grl-tracker-utils.h"
+
+/* --------- Logging -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT tracker_source_log_domain
+GRL_LOG_DOMAIN_STATIC(tracker_source_log_domain);
+
+/* ------- Definitions ------- */
+
+#define MEDIA_TYPE "grilo-media-type"
+
+#define TRACKER_ITEM_CACHE_SIZE (10000)
+
+/* --- Other --- */
+
+enum {
+ PROP_0,
+ PROP_TRACKER_CONNECTION,
+ PROP_TRACKER_DATASOURCE,
+};
+
+static void grl_tracker_source_set_property (GObject *object,
+ guint propid,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void grl_tracker_source_finalize (GObject *object);
+
+/* ===================== Globals ================= */
+
+/* shared data across */
+GrlTrackerCache *grl_tracker_item_cache;
+GHashTable *grl_tracker_source_sources;
+
+/* ================== TrackerSource GObject ================ */
+
+G_DEFINE_TYPE (GrlTrackerSource, grl_tracker_source, GRL_TYPE_SOURCE);
+
+static GrlTrackerSource *
+grl_tracker_source_new (TrackerSparqlConnection *connection)
+{
+ GRL_DEBUG ("%s", __FUNCTION__);
+
+ return g_object_new (GRL_TRACKER_SOURCE_TYPE,
+ "source-id", GRL_TRACKER_SOURCE_ID,
+ "source-name", GRL_TRACKER_SOURCE_NAME,
+ "source-desc", GRL_TRACKER_SOURCE_DESC,
+ "tracker-connection", connection,
+ "tracker-datasource", "",
+ NULL);
+}
+
+static void
+grl_tracker_source_class_init (GrlTrackerSourceClass * klass)
+{
+ GObjectClass *g_class = G_OBJECT_CLASS (klass);
+ GrlSourceClass *source_class = GRL_SOURCE_CLASS (klass);
+
+ g_class->finalize = grl_tracker_source_finalize;
+ g_class->set_property = grl_tracker_source_set_property;
+
+ source_class->cancel = grl_tracker_source_cancel;
+ source_class->supported_keys = grl_tracker_supported_keys;
+ source_class->writable_keys = grl_tracker_source_writable_keys;
+ source_class->store_metadata = grl_tracker_source_store_metadata;
+ source_class->query = grl_tracker_source_query;
+ source_class->resolve = grl_tracker_source_resolve;
+ source_class->may_resolve = grl_tracker_source_may_resolve;
+ source_class->search = grl_tracker_source_search;
+ source_class->browse = grl_tracker_source_browse;
+ source_class->notify_change_start = grl_tracker_source_change_start;
+ source_class->notify_change_stop = grl_tracker_source_change_stop;
+ source_class->supported_operations = grl_tracker_source_supported_operations;
+ source_class->get_caps = grl_tracker_source_get_caps;
+ source_class->test_media_from_uri = grl_tracker_source_test_media_from_uri;
+ source_class->media_from_uri = grl_tracker_source_get_media_from_uri;
+
+ g_object_class_install_property (g_class,
+ PROP_TRACKER_CONNECTION,
+ g_param_spec_object ("tracker-connection",
+ "tracker connection",
+ "A Tracker connection",
+ TRACKER_SPARQL_TYPE_CONNECTION,
+ G_PARAM_WRITABLE
+ | G_PARAM_CONSTRUCT_ONLY
+ | G_PARAM_STATIC_NAME));
+
+ g_object_class_install_property (g_class,
+ PROP_TRACKER_DATASOURCE,
+ g_param_spec_string ("tracker-datasource",
+ "tracker datasource",
+ "A Tracker nie:DataSource URN",
+ NULL,
+ G_PARAM_WRITABLE
+ | G_PARAM_CONSTRUCT_ONLY
+ | G_PARAM_STATIC_NAME));
+
+ g_type_class_add_private (klass, sizeof (GrlTrackerSourcePriv));
+}
+
+static void
+grl_tracker_source_init (GrlTrackerSource *source)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+ source->priv = priv;
+
+ priv->operations = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+grl_tracker_source_finalize (GObject *object)
+{
+ GrlTrackerSource *self;
+
+ self = GRL_TRACKER_SOURCE (object);
+
+ g_clear_object (&self->priv->tracker_connection);
+
+ G_OBJECT_CLASS (grl_tracker_source_parent_class)->finalize (object);
+}
+
+static void
+grl_tracker_source_set_property (GObject *object,
+ guint propid,
+ const GValue *value,
+ GParamSpec *pspec)
+
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (object);
+
+ switch (propid) {
+ case PROP_TRACKER_CONNECTION:
+ g_clear_object (&priv->tracker_connection);
+ priv->tracker_connection = g_object_ref (g_value_get_object (value));
+ break;
+
+ case PROP_TRACKER_DATASOURCE:
+ g_clear_pointer (&priv->tracker_datasource, g_free);
+ priv->tracker_datasource = g_strdup (g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
+ }
+}
+
+const gchar *
+grl_tracker_source_get_tracker_source (GrlTrackerSource *source)
+{
+ GrlTrackerSourcePriv *priv;
+
+ g_return_val_if_fail (GRL_IS_TRACKER_SOURCE (source), NULL);
+
+ priv = source->priv;
+
+ return priv->tracker_datasource;
+}
+
+TrackerSparqlConnection *
+grl_tracker_source_get_tracker_connection (GrlTrackerSource *source)
+{
+ GrlTrackerSourcePriv *priv;
+
+ g_return_val_if_fail (GRL_IS_TRACKER_SOURCE (source), NULL);
+
+ priv = source->priv;
+
+ return priv->tracker_connection;
+}
+
+/* =================== TrackerSource Plugin =============== */
+
+void
+grl_tracker_add_source (GrlTrackerSource *source)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+ GRL_DEBUG ("====================>add source '%s'",
+ grl_source_get_name (GRL_SOURCE (source)));
+
+ g_hash_table_insert (grl_tracker_source_sources,
+ (gpointer) grl_tracker_source_get_tracker_source (source),
+ g_object_ref (source));
+ priv->state = GRL_TRACKER_SOURCE_STATE_RUNNING;
+ grl_registry_register_source (grl_registry_get_default (),
+ grl_tracker_plugin,
+ GRL_SOURCE (g_object_ref (source)),
+ NULL);
+}
+
+void
+grl_tracker_del_source (GrlTrackerSource *source)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+ GRL_DEBUG ("==================>del source '%s'",
+ grl_source_get_name (GRL_SOURCE (source)));
+
+ g_hash_table_remove (grl_tracker_source_sources,
+ grl_tracker_source_get_tracker_source (source));
+ grl_tracker_source_cache_del_source (grl_tracker_item_cache, source);
+ priv->state = GRL_TRACKER_SOURCE_STATE_DELETED;
+ grl_registry_unregister_source (grl_registry_get_default (),
+ GRL_SOURCE (source),
+ NULL);
+}
+
+gboolean
+grl_tracker_source_can_notify (GrlTrackerSource *source)
+{
+ GrlTrackerSourcePriv *priv = GRL_TRACKER_SOURCE_GET_PRIVATE (source);
+
+ if (priv->state == GRL_TRACKER_SOURCE_STATE_RUNNING)
+ return priv->notify_changes;
+
+ return FALSE;
+}
+
+GrlTrackerSource *
+grl_tracker_source_find (const gchar *id)
+{
+ GrlTrackerSource *source;
+
+ source = g_hash_table_lookup (grl_tracker_source_sources, id);
+ return source;
+}
+
+static gboolean
+match_plugin_id (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ if (g_strcmp0 (grl_source_get_id (GRL_SOURCE (value)),
+ (gchar *) user_data) == 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Search for registered plugin with @id */
+GrlTrackerSource *
+grl_tracker_source_find_source (const gchar *id)
+{
+ GrlTrackerSource *source;
+
+ source = g_hash_table_find (grl_tracker_source_sources,
+ match_plugin_id,
+ (gpointer) id);
+ return source;
+}
+
+void
+grl_tracker_source_sources_init (void)
+{
+
+ GRL_LOG_DOMAIN_INIT (tracker_source_log_domain, "tracker-source");
+
+ GRL_DEBUG ("%s", __FUNCTION__);
+
+ grl_tracker_item_cache =
+ grl_tracker_source_cache_new (TRACKER_ITEM_CACHE_SIZE);
+ grl_tracker_source_sources = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+
+ if (grl_tracker_connection != NULL) {
+ GrlTrackerSource *source;
+
+ grl_tracker_notify_init (grl_tracker_connection);
+
+ /* One source to rule them all. */
+ source = grl_tracker_source_new (grl_tracker_connection);
+ grl_tracker_add_source (source);
+ g_object_unref (source);
+ }
+}
diff --git a/src/tracker3/grl-tracker-source.h b/src/tracker3/grl-tracker-source.h
new file mode 100644
index 0000000..a615726
--- /dev/null
+++ b/src/tracker3/grl-tracker-source.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_SOURCE_H_
+#define _GRL_TRACKER_SOURCE_H_
+
+#include <grilo.h>
+#include <tracker-sparql.h>
+
+#define GRL_TRACKER_SOURCE_TYPE \
+ (grl_tracker_source_get_type ())
+
+#define GRL_TRACKER_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GRL_TRACKER_SOURCE_TYPE, \
+ GrlTrackerSource))
+
+#define GRL_IS_TRACKER_SOURCE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ GRL_TRACKER_SOURCE_TYPE))
+
+#define GRL_TRACKER_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), \
+ GRL_TRACKER_SOURCE_TYPE, \
+ GrlTrackerSourceClass))
+
+#define GRL_IS_TRACKER_SOURCE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), \
+ GRL_TRACKER_SOURCE_TYPE))
+
+#define GRL_TRACKER_SOURCE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GRL_TRACKER_SOURCE_TYPE, \
+ GrlTrackerSourceClass))
+
+typedef struct _GrlTrackerSource GrlTrackerSource;
+typedef struct _GrlTrackerSourcePriv GrlTrackerSourcePriv;
+
+struct _GrlTrackerSource {
+
+ GrlSource parent;
+
+ /*< private >*/
+ GrlTrackerSourcePriv *priv;
+
+};
+
+typedef struct _GrlTrackerSourceClass GrlTrackerSourceClass;
+
+struct _GrlTrackerSourceClass {
+
+ GrlSourceClass parent_class;
+
+};
+
+GType grl_tracker_source_get_type (void);
+
+gboolean grl_tracker_source_can_notify (GrlTrackerSource *source);
+
+const gchar *grl_tracker_source_get_tracker_source (GrlTrackerSource *source);
+
+TrackerSparqlConnection *grl_tracker_source_get_tracker_connection (GrlTrackerSource *source);
+
+/**/
+
+void grl_tracker_source_sources_init (void);
+
+void grl_tracker_add_source (GrlTrackerSource *source);
+
+void grl_tracker_del_source (GrlTrackerSource *source);
+
+GrlTrackerSource *grl_tracker_source_find (const gchar *id);
+
+GrlTrackerSource *grl_tracker_source_find_source (const gchar *id);
+
+#endif /* _GRL_TRACKER_SOURCE_H_ */
diff --git a/src/tracker3/grl-tracker-utils.c b/src/tracker3/grl-tracker-utils.c
new file mode 100644
index 0000000..e83ec5e
--- /dev/null
+++ b/src/tracker3/grl-tracker-utils.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "grl-tracker-utils.h"
+#include <glib/gi18n-lib.h>
+
+/**/
+
+static GHashTable *grl_to_sparql_mapping = NULL;
+static GHashTable *sparql_to_grl_mapping = NULL;
+
+GrlKeyID grl_metadata_key_tracker_urn;
+GrlKeyID grl_metadata_key_gibest_hash;
+
+
+/**/
+
+static gchar *
+build_flavored_key (gchar *key, const gchar *flavor)
+{
+ gint i = 0;
+
+ while (key[i] != '\0') {
+ if (!g_ascii_isalnum (key[i])) {
+ key[i] = '_';
+ }
+ i++;
+ }
+
+ return g_strdup_printf ("%s_%s", key, flavor);
+}
+
+static void
+set_orientation (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gchar *str = tracker_sparql_cursor_get_string (cursor, column, NULL);
+
+ if (g_str_has_suffix (str, "nfo#orientation-top"))
+ grl_data_set_int (GRL_DATA (media), key, 0);
+ else if (g_str_has_suffix (str, "nfo#orientation-right"))
+ grl_data_set_int (GRL_DATA (media), key, 90);
+ else if (g_str_has_suffix (str, "nfo#orientation-bottom"))
+ grl_data_set_int (GRL_DATA (media), key, 180);
+ else if (g_str_has_suffix (str, "nfo#orientation-left"))
+ grl_data_set_int (GRL_DATA (media), key, 270);
+}
+
+static void
+set_date (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gchar *str = tracker_sparql_cursor_get_string (cursor, column, NULL);
+ if (key == GRL_METADATA_KEY_CREATION_DATE
+ || key == GRL_METADATA_KEY_LAST_PLAYED
+ || key == GRL_METADATA_KEY_MODIFICATION_DATE) {
+ GDateTime *date = grl_date_time_from_iso8601 (str);
+ if (date) {
+ grl_data_set_boxed (GRL_DATA (media), key, date);
+ g_date_time_unref (date);
+ }
+ }
+}
+
+static void
+set_favourite (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gchar *str = tracker_sparql_cursor_get_string (cursor, column, NULL);
+ gboolean is_favourite = FALSE;
+
+ if (str != NULL && g_str_has_suffix (str, "predefined-tag-favorite"))
+ is_favourite = TRUE;
+
+ grl_data_set_boolean (GRL_DATA (media), key, is_favourite);
+}
+
+static void
+set_title_from_filename (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gchar *str = tracker_sparql_cursor_get_string (cursor, column, NULL);
+ if (key == GRL_METADATA_KEY_TITLE) {
+ grl_data_set_boolean (GRL_DATA (media), GRL_METADATA_KEY_TITLE_FROM_FILENAME, TRUE);
+ grl_media_set_title (media, str);
+ }
+}
+
+static void
+set_title (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gchar *str = tracker_sparql_cursor_get_string (cursor, column, NULL);
+ grl_data_set_boolean (GRL_DATA (media), GRL_METADATA_KEY_TITLE_FROM_FILENAME, FALSE);
+ grl_media_set_title (media, str);
+}
+
+static void
+set_string_metadata_keys (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gchar *str = tracker_sparql_cursor_get_string (cursor, column, NULL);
+ grl_data_set_string (GRL_DATA (media), key, str);
+}
+
+static void
+set_int_metadata_keys (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key)
+{
+ const gint64 value = tracker_sparql_cursor_get_integer (cursor, column);
+ grl_data_set_int (GRL_DATA (media), key, value);
+}
+
+static tracker_grl_sparql_t *
+insert_key_mapping (GrlKeyID grl_key,
+ const gchar *sparql_key_attr,
+ const gchar *sparql_key_attr_call,
+ const gchar *sparql_key_flavor)
+{
+ tracker_grl_sparql_t *assoc;
+ GList *assoc_list;
+ gchar *canon_name;
+
+ g_return_val_if_fail (grl_key != GRL_METADATA_KEY_INVALID, NULL);
+
+ assoc = g_new0 (tracker_grl_sparql_t, 1);
+ assoc_list = g_hash_table_lookup (grl_to_sparql_mapping,
+ GRLKEYID_TO_POINTER (grl_key));
+ canon_name = g_strdup (GRL_METADATA_KEY_GET_NAME (grl_key));
+
+ assoc->grl_key = grl_key;
+ assoc->sparql_key_name = build_flavored_key (canon_name,
+ sparql_key_flavor);
+ assoc->sparql_key_name_canon = g_strdup (canon_name);
+ assoc->sparql_key_attr = sparql_key_attr;
+ assoc->sparql_key_attr_call = sparql_key_attr_call;
+ assoc->sparql_key_flavor = sparql_key_flavor;
+
+ assoc_list = g_list_append (assoc_list, assoc);
+
+ g_hash_table_insert (grl_to_sparql_mapping,
+ GRLKEYID_TO_POINTER (grl_key),
+ assoc_list);
+ g_hash_table_insert (sparql_to_grl_mapping,
+ (gpointer) assoc->sparql_key_name,
+ assoc);
+ g_hash_table_insert (sparql_to_grl_mapping,
+ (gpointer) GRL_METADATA_KEY_GET_NAME (grl_key),
+ assoc);
+
+ /* Grilo maps key names to SPARQL variables. Key names can contain dashes,
+ * however SPARQL does not allow dashes in variable names. So use the to
+ * underscores converted canon_name as additional mapping.
+ */
+ if (g_strrstr (assoc->sparql_key_name_canon, "_")) {
+ g_hash_table_insert (sparql_to_grl_mapping,
+ (gpointer) assoc->sparql_key_name_canon,
+ assoc);
+ }
+
+ g_free (canon_name);
+
+ return assoc;
+}
+
+static tracker_grl_sparql_t *
+insert_key_mapping_with_setter (GrlKeyID grl_key,
+ const gchar *sparql_key_attr,
+ const gchar *sparql_key_attr_call,
+ const gchar *sparql_key_flavor,
+ tracker_grl_sparql_setter_cb_t setter)
+{
+ tracker_grl_sparql_t *assoc;
+
+ assoc = insert_key_mapping (grl_key,
+ sparql_key_attr,
+ sparql_key_attr_call,
+ sparql_key_flavor);
+
+ assoc->set_value = setter;
+
+ return assoc;
+}
+
+void
+grl_tracker_setup_key_mappings (void)
+{
+ GrlRegistry *registry = grl_registry_get_default ();
+ GrlKeyID grl_metadata_key_chromaprint;
+
+ grl_metadata_key_tracker_urn =
+ grl_registry_lookup_metadata_key (registry, "tracker-urn");
+
+ grl_metadata_key_gibest_hash =
+ grl_registry_lookup_metadata_key (registry, "gibest-hash");
+
+ grl_metadata_key_chromaprint =
+ grl_registry_lookup_metadata_key (registry, "chromaprint");
+
+ grl_to_sparql_mapping = g_hash_table_new (g_direct_hash, g_direct_equal);
+ sparql_to_grl_mapping = g_hash_table_new (g_str_hash, g_str_equal);
+
+ insert_key_mapping (grl_metadata_key_tracker_urn,
+ NULL,
+ "?urn",
+ "file");
+
+ insert_key_mapping (GRL_METADATA_KEY_ALBUM,
+ NULL,
+ "nie:title(nmm:musicAlbum(?urn))",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_ALBUM_DISC_NUMBER,
+ NULL,
+ "nmm:setNumber(nmm:musicAlbumDisc(?urn))",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_ARTIST,
+ NULL,
+ "nmm:artistName(nmm:performer(?urn))",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_ALBUM_ARTIST,
+ NULL,
+ "nmm:artistName(nmm:albumArtist(nmm:musicAlbum(?urn)))",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_AUTHOR,
+ NULL,
+ "nmm:artistName(nmm:performer(?urn))",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_BITRATE,
+ "nfo:averageBitrate",
+ "nfo:averageBitrate(?urn)",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_CHILDCOUNT,
+ "nfo:entryCounter",
+ "nfo:entryCounter(?urn)",
+ "directory");
+
+ insert_key_mapping (GRL_METADATA_KEY_COMPOSER,
+ NULL,
+ "nmm:artistName(nmm:composer(?urn))",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_SIZE,
+ NULL,
+ "nfo:fileSize(?urn)",
+ "file");
+
+ insert_key_mapping (grl_metadata_key_gibest_hash,
+ NULL,
+ "(select nfo:hashValue(?h) { ?urn nfo:hasHash ?h . ?h nfo:hashAlgorithm \"gibest\" })",
+ "video");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_MODIFICATION_DATE,
+ "nfo:fileLastModified",
+ "nfo:fileLastModified(?urn)",
+ "file",
+ set_date);
+
+ insert_key_mapping (GRL_METADATA_KEY_DURATION,
+ "nfo:duration",
+ "nfo:duration(?urn)",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_MB_TRACK_ID,
+ NULL,
+ "(SELECT tracker:referenceIdentifier(?t) AS ?t_id { ?urn tracker:hasExternalReference ?t . ?t tracker:referenceSource \"https://musicbrainz.org/doc/Track\" })",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_MB_ARTIST_ID,
+ NULL,
+ "(SELECT tracker:referenceIdentifier(?a) AS ?a_id { ?urn nmm:performer ?artist . ?artist tracker:hasExternalReference ?a . ?a tracker:referenceSource \"https://musicbrainz.org/doc/Artist\" })",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_MB_RECORDING_ID,
+ NULL,
+ "(SELECT tracker:referenceIdentifier(?r) AS ?r_id { ?urn tracker:hasExternalReference ?r . ?r tracker:referenceSource \"https://musicbrainz.org/doc/Recording\" })",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_MB_RELEASE_ID,
+ NULL,
+ "(SELECT tracker:referenceIdentifier(?re) AS ?re_id { ?urn nmm:musicAlbum ?album . ?album tracker:hasExternalReference ?re . ?re tracker:referenceSource \"https://musicbrainz.org/doc/Release\" })",
+ "audio");
+
+ insert_key_mapping (GRL_METADATA_KEY_MB_RELEASE_GROUP_ID,
+ NULL,
+ "(SELECT tracker:referenceIdentifier(?rg) AS ?rg_id { ?urn nmm:musicAlbum ?album . ?album tracker:hasExternalReference ?rg . ?rg tracker:referenceSource \"https://musicbrainz.org/doc/Release_Group\" })",
+ "audio");
+
+ if (grl_metadata_key_chromaprint != 0) {
+ insert_key_mapping_with_setter (grl_metadata_key_chromaprint,
+ NULL,
+ "(select nfo:hashValue(?h) { ?urn nfo:hasHash ?h . ?h nfo:hashAlgorithm \"chromaprint\" })",
+ "audio",
+ set_string_metadata_keys);
+ };
+
+ insert_key_mapping (GRL_METADATA_KEY_FRAMERATE,
+ "nfo:frameRate",
+ "nfo:frameRate(?urn)",
+ "video");
+
+ insert_key_mapping (GRL_METADATA_KEY_HEIGHT,
+ "nfo:height",
+ "nfo:height(?urn)",
+ "video");
+
+ insert_key_mapping (GRL_METADATA_KEY_ID,
+ "tracker:id",
+ "tracker:id(?urn)",
+ "file");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_LAST_PLAYED,
+ "nfo:fileLastAccessed",
+ "nfo:fileLastAccessed(?urn)",
+ "file",
+ set_date);
+
+ insert_key_mapping (GRL_METADATA_KEY_MIME,
+ "nie:mimeType",
+ "nie:mimeType(?urn)",
+ "file");
+
+ insert_key_mapping (GRL_METADATA_KEY_SITE,
+ "nie:url",
+ "nie:url(?urn)",
+ "file");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_TITLE,
+ "nie:title",
+ "nie:title(?urn)",
+ "audio",
+ set_title);
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_TITLE,
+ "nfo:fileName",
+ "nfo:fileName(?urn)",
+ "file",
+ set_title_from_filename);
+
+ insert_key_mapping (GRL_METADATA_KEY_URL,
+ "nie:url",
+ "nie:url(?urn)",
+ "file");
+
+ insert_key_mapping (GRL_METADATA_KEY_WIDTH,
+ "nfo:width",
+ "nfo:width(?urn)",
+ "video");
+
+ insert_key_mapping (GRL_METADATA_KEY_SEASON,
+ "nmm:season",
+ "nmm:season(?urn)",
+ "video");
+
+ insert_key_mapping (GRL_METADATA_KEY_EPISODE,
+ "nmm:episodeNumber",
+ "nmm:episodeNumber(?urn)",
+ "video");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_CREATION_DATE,
+ "nie:contentCreated",
+ "nie:contentCreated(?urn)",
+ "image",
+ set_date);
+
+ insert_key_mapping (GRL_METADATA_KEY_CAMERA_MODEL,
+ NULL,
+ "nfo:model(nfo:equipment(?urn))",
+ "image");
+
+ insert_key_mapping (GRL_METADATA_KEY_FLASH_USED,
+ "nmm:flash",
+ "nmm:flash(?urn)",
+ "image");
+
+ insert_key_mapping (GRL_METADATA_KEY_EXPOSURE_TIME,
+ "nmm:exposureTime",
+ "nmm:exposureTime(?urn)",
+ "image");
+
+ insert_key_mapping (GRL_METADATA_KEY_ISO_SPEED,
+ "nmm:isoSpeed",
+ "nmm:isoSpeed(?urn)",
+ "image");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_ORIENTATION,
+ "nfo:orientation",
+ "nfo:orientation(?urn)",
+ "image",
+ set_orientation);
+
+ insert_key_mapping (GRL_METADATA_KEY_PLAY_COUNT,
+ "nie:usageCounter",
+ "nie:usageCounter(?urn)",
+ "media");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_LAST_PLAYED,
+ "nie:contentAccessed",
+ "nie:contentAccessed(?urn)",
+ "media",
+ set_date);
+
+ insert_key_mapping (GRL_METADATA_KEY_LAST_POSITION,
+ "nfo:lastPlayedPosition",
+ "nfo:lastPlayedPosition(?urn)",
+ "media");
+
+ insert_key_mapping (GRL_METADATA_KEY_START_TIME,
+ "nfo:audioOffset",
+ "nfo:audioOffset(?urn)",
+ "media");
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_TRACK_NUMBER,
+ "nmm:trackNumber",
+ "nmm:trackNumber(?urn)",
+ "audio",
+ set_int_metadata_keys);
+
+ insert_key_mapping_with_setter (GRL_METADATA_KEY_FAVOURITE,
+ "nao:hasTag",
+ "nao:hasTag(?urn)",
+ "audio",
+ set_favourite);
+}
+
+tracker_grl_sparql_t *
+grl_tracker_get_mapping_from_sparql (const gchar *key)
+{
+ return (tracker_grl_sparql_t *) g_hash_table_lookup (sparql_to_grl_mapping,
+ key);
+}
+
+static GList *
+get_mapping_from_grl (const GrlKeyID key)
+{
+ return (GList *) g_hash_table_lookup (grl_to_sparql_mapping,
+ GRLKEYID_TO_POINTER (key));
+}
+
+gboolean
+grl_tracker_key_is_supported (const GrlKeyID key)
+{
+ return g_hash_table_lookup (grl_to_sparql_mapping,
+ GRLKEYID_TO_POINTER (key)) != NULL;
+}
+
+/**/
+
+gchar *
+grl_tracker_source_get_device_constraint (GrlTrackerSourcePriv *priv)
+{
+ if (priv->tracker_datasource == NULL ||
+ priv->tracker_datasource[0] == '\0')
+ return g_strdup ("");
+
+ return g_strdup_printf ("?urn nie:dataSource <%s> .",
+ priv->tracker_datasource);
+}
+
+gchar *
+grl_tracker_source_get_select_string (const GList *keys)
+{
+ const GList *key = keys;
+ GString *gstr = g_string_new ("");
+ GList *assoc_list;
+ tracker_grl_sparql_t *assoc;
+
+ assoc_list = get_mapping_from_grl (grl_metadata_key_tracker_urn);
+ assoc = (tracker_grl_sparql_t *) assoc_list->data;
+ g_string_append_printf (gstr, "%s AS ?%s ",
+ assoc->sparql_key_attr_call,
+ assoc->sparql_key_name);
+
+ while (key != NULL) {
+ assoc_list = get_mapping_from_grl (GRLPOINTER_TO_KEYID (key->data));
+ while (assoc_list != NULL) {
+ assoc = (tracker_grl_sparql_t *) assoc_list->data;
+ if (assoc != NULL) {
+ g_string_append_printf (gstr, "%s AS ?%s ",
+ assoc->sparql_key_attr_call,
+ assoc->sparql_key_name);
+ }
+ assoc_list = assoc_list->next;
+ }
+ key = key->next;
+ }
+
+ return g_string_free (gstr, FALSE);
+}
+
+static void
+gen_prop_insert_string (GString *gstr,
+ tracker_grl_sparql_t *assoc,
+ GrlData *data)
+{
+ gchar *tmp;
+ GType type = GRL_METADATA_KEY_GET_TYPE (assoc->grl_key);
+
+ switch (type) {
+ case G_TYPE_STRING:
+ tmp = g_strescape (grl_data_get_string (data, assoc->grl_key), NULL);
+ g_string_append_printf (gstr, "%s \"%s\"",
+ assoc->sparql_key_attr, tmp);
+ g_free (tmp);
+ break;
+
+ case G_TYPE_INT:
+ g_string_append_printf (gstr, "%s %i",
+ assoc->sparql_key_attr,
+ grl_data_get_int (data, assoc->grl_key));
+ break;
+
+ case G_TYPE_FLOAT:
+ g_string_append_printf (gstr, "%s %f",
+ assoc->sparql_key_attr,
+ grl_data_get_float (data, assoc->grl_key));
+ break;
+
+ case G_TYPE_BOOLEAN:
+ /* Special case for favourite tag, see comment in
+ * grl_tracker_tracker_get_insert_string for more details.
+ */
+ if (assoc->grl_key == GRL_METADATA_KEY_FAVOURITE) {
+ g_string_append_printf (gstr, "%s nao:predefined-tag-favorite",
+ assoc->sparql_key_attr);
+ }
+ break;
+
+ default:
+ if (type == G_TYPE_DATE_TIME) {
+ tmp = g_date_time_format (grl_data_get_boxed (data, assoc->grl_key),
+ "%FT%T%:z");
+ g_string_append_printf (gstr, "%s '%s'",
+ assoc->sparql_key_attr,
+ tmp);
+
+ g_free (tmp);
+ }
+ break;
+ }
+}
+
+gchar *
+grl_tracker_tracker_get_insert_string (GrlMedia *media, const GList *keys)
+{
+ gboolean first = TRUE;
+ const GList *key;
+ GString *gstr = g_string_new ("");
+
+ for (key = keys; key != NULL; key = key->next) {
+ const GList *assoc_list;
+ GrlKeyID key_id = GRLPOINTER_TO_KEYID (key->data);
+
+ for (assoc_list = get_mapping_from_grl (key_id);
+ assoc_list != NULL;
+ assoc_list = assoc_list->next) {
+ tracker_grl_sparql_t *assoc = assoc_list->data;
+
+ if (assoc == NULL)
+ continue;
+
+ /* The favourite key is really setting or deleting a tag
+ * in tracker, so in the case of setting it to false skip
+ * the insert string creation step for this key completely.
+ */
+ if (assoc->grl_key == GRL_METADATA_KEY_FAVOURITE &&
+ !grl_media_get_favourite (media))
+ continue;
+
+ if (!grl_data_has_key (GRL_DATA (media), key_id))
+ continue;
+
+ /* Special case for key title, nfo:fileName is read-only.
+ * It cannot be modified.
+ */
+ if (assoc->grl_key == GRL_METADATA_KEY_TITLE &&
+ g_strcmp0 (assoc->sparql_key_attr, "nfo:fileName") == 0) {
+ continue;
+ }
+
+ if (!first)
+ g_string_append (gstr, " ; ");
+
+ gen_prop_insert_string (gstr, assoc, GRL_DATA (media));
+ first = FALSE;
+ }
+ }
+
+ return g_string_free (gstr, FALSE);
+}
+
+gchar *
+grl_tracker_get_delete_string (const GList *keys)
+{
+ gboolean first = TRUE;
+ const GList *key = keys, *assoc_list;
+ tracker_grl_sparql_t *assoc;
+ GString *gstr = g_string_new ("");
+ gchar *ret;
+ gint var_n = 0;
+
+ while (key != NULL) {
+ assoc_list = get_mapping_from_grl (GRLPOINTER_TO_KEYID (key->data));
+ while (assoc_list != NULL) {
+ assoc = (tracker_grl_sparql_t *) assoc_list->data;
+ if (assoc != NULL) {
+ /* Special case for key title, nfo:fileName is read-only.
+ * It cannot be modified.
+ */
+ if (assoc->grl_key == GRL_METADATA_KEY_TITLE &&
+ g_strcmp0 (assoc->sparql_key_attr, "nfo:fileName") == 0) {
+ assoc_list = assoc_list->next;
+ continue;
+ }
+
+ if (first) {
+ g_string_append_printf (gstr, "%s ?v%i",
+ assoc->sparql_key_attr, var_n);
+ first = FALSE;
+ } else {
+ g_string_append_printf (gstr, " ; %s ?v%i",
+ assoc->sparql_key_attr, var_n);
+ }
+ var_n++;
+ }
+ assoc_list = assoc_list->next;
+ }
+ key = key->next;
+ }
+
+ ret = gstr->str;
+ g_string_free (gstr, FALSE);
+
+ return ret;
+}
+
+gchar *
+grl_tracker_get_delete_conditional_string (const gchar *urn,
+ const GList *keys)
+{
+ gboolean first = TRUE;
+ const GList *key = keys, *assoc_list;
+ tracker_grl_sparql_t *assoc;
+ GString *gstr = g_string_new ("");
+ gchar *ret;
+ gint var_n = 0;
+
+ while (key != NULL) {
+ assoc_list = get_mapping_from_grl (GRLPOINTER_TO_KEYID (key->data));
+ while (assoc_list != NULL) {
+ assoc = (tracker_grl_sparql_t *) assoc_list->data;
+ if (assoc != NULL) {
+ /* Special case for key title, nfo:fileName is read-only.
+ * It cannot be modified.
+ */
+ if (assoc->grl_key == GRL_METADATA_KEY_TITLE &&
+ g_strcmp0 (assoc->sparql_key_attr, "nfo:fileName") == 0) {
+ assoc_list = assoc_list->next;
+ continue;
+ }
+
+ if (first) {
+ g_string_append_printf (gstr, "OPTIONAL { <%s> %s ?v%i }",
+ urn, assoc->sparql_key_attr, var_n);
+ first = FALSE;
+ } else {
+ g_string_append_printf (gstr, " . OPTIONAL { <%s> %s ?v%i }",
+ urn, assoc->sparql_key_attr, var_n);
+ }
+ var_n++;
+ }
+ assoc_list = assoc_list->next;
+ }
+ key = key->next;
+ }
+
+ ret = gstr->str;
+ g_string_free (gstr, FALSE);
+
+ return ret;
+}
+
+static GrlMedia *
+grl_tracker_build_grilo_media_default (GHashTable *ht)
+{
+ if (g_hash_table_lookup (ht, RDF_TYPE_MUSIC)) {
+ return grl_media_audio_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_VIDEO)) {
+ return grl_media_video_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_IMAGE)) {
+ return grl_media_image_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_ARTIST)) {
+ return grl_media_container_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_ALBUM)) {
+ return grl_media_container_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_CONTAINER)) {
+ return grl_media_container_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_FOLDER)) {
+ return grl_media_container_new ();
+ } else if (g_hash_table_lookup (ht, RDF_TYPE_PLAYLIST)) {
+ return grl_media_container_new ();
+ }
+
+ return NULL;
+}
+
+/**/
+
+/* Builds an appropriate GrlMedia based on ontology type returned by
+ tracker, or NULL if unknown */
+GrlMedia *
+grl_tracker_build_grilo_media (const gchar *rdf_type,
+ GrlTypeFilter type_filter)
+{
+ GrlMedia *media = NULL;
+ gchar **rdf_single_type;
+ int i;
+ GHashTable *ht;
+
+ 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;
+ ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ for (; i>= 0; i--)
+ g_hash_table_insert (ht, g_path_get_basename (rdf_single_type[i]), GINT_TO_POINTER(TRUE));
+
+ if (type_filter == GRL_TYPE_FILTER_NONE ||
+ type_filter == GRL_TYPE_FILTER_ALL) {
+ media = grl_tracker_build_grilo_media_default (ht);
+ } else if ((type_filter & GRL_TYPE_FILTER_AUDIO) &&
+ g_hash_table_lookup (ht, RDF_TYPE_MUSIC)) {
+ media = grl_media_audio_new ();
+ } else if ((type_filter & GRL_TYPE_FILTER_VIDEO) &&
+ g_hash_table_lookup (ht, RDF_TYPE_VIDEO)) {
+ media = grl_media_video_new ();
+ } else if ((type_filter & GRL_TYPE_FILTER_IMAGE) &&
+ g_hash_table_lookup (ht, RDF_TYPE_IMAGE)) {
+ media = grl_media_image_new ();
+ } else {
+ media = grl_tracker_build_grilo_media_default (ht);
+ }
+
+ g_hash_table_destroy (ht);
+ g_strfreev (rdf_single_type);
+
+ if (!media)
+ media = grl_media_new ();
+
+ return media;
+}
+
+/**/
+
+static gchar *
+get_tracker_volume_name (const gchar *uri,
+ const gchar *datasource)
+{
+ gchar *source_name = NULL;
+ GVolumeMonitor *volume_monitor;
+ GList *mounts, *mount;
+ GFile *file;
+
+ if (uri != NULL && *uri != '\0') {
+ volume_monitor = g_volume_monitor_get ();
+ mounts = g_volume_monitor_get_mounts (volume_monitor);
+ file = g_file_new_for_uri (uri);
+
+ mount = mounts;
+ while (mount != NULL) {
+ GFile *m_file = g_mount_get_root (G_MOUNT (mount->data));
+
+ if (g_file_equal (m_file, file)) {
+ gchar *m_name = g_mount_get_name (G_MOUNT (mount->data));
+ g_object_unref (G_OBJECT (m_file));
+ source_name = g_strdup_printf (_("Removable — %s"), m_name);
+ g_free (m_name);
+ break;
+ }
+ g_object_unref (G_OBJECT (m_file));
+
+ mount = mount->next;
+ }
+ g_list_free_full (mounts, g_object_unref);
+ g_object_unref (G_OBJECT (file));
+ g_object_unref (G_OBJECT (volume_monitor));
+ } else {
+ source_name = g_strdup (_("Local files"));
+ }
+
+ return source_name;
+}
+
+gchar *
+grl_tracker_get_source_name (const gchar *rdf_type,
+ const gchar *uri,
+ const gchar *datasource,
+ const gchar *datasource_name)
+{
+ gchar *source_name = NULL;
+ gchar **rdf_single_type;
+ gint i;
+
+ /* 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 (i >= 0) {
+ if (g_str_has_suffix (rdf_single_type[i], RDF_TYPE_VOLUME)) {
+ source_name = get_tracker_volume_name (uri, datasource);
+ break;
+ }
+ i--;
+ }
+
+ g_strfreev (rdf_single_type);
+
+ return source_name;
+}
+
+const GList *
+grl_tracker_supported_keys (GrlSource *source)
+{
+ static GList *supported_keys = NULL;
+
+ if (!supported_keys) {
+ supported_keys = g_hash_table_get_keys (grl_to_sparql_mapping);
+ }
+
+ return supported_keys;
+}
diff --git a/src/tracker3/grl-tracker-utils.h b/src/tracker3/grl-tracker-utils.h
new file mode 100644
index 0000000..bf6c0ef
--- /dev/null
+++ b/src/tracker3/grl-tracker-utils.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_UTILS_H_
+#define _GRL_TRACKER_UTILS_H_
+
+#include "grl-tracker-source-priv.h"
+
+/* ------- Definitions ------- */
+
+#define RDF_TYPE_ALBUM "nmm#MusicAlbum"
+#define RDF_TYPE_ARTIST "nmm#Artist"
+#define RDF_TYPE_AUDIO "nfo#Audio"
+#define RDF_TYPE_MUSIC "nmm#MusicPiece"
+#define RDF_TYPE_IMAGE "nmm#Photo"
+#define RDF_TYPE_VIDEO "nmm#Video"
+#define RDF_TYPE_FOLDER "nfo#Folder"
+#define RDF_TYPE_DOCUMENT "nfo#Document"
+#define RDF_TYPE_CONTAINER "grilo#Container"
+#define RDF_TYPE_PLAYLIST "nmm#Playlist"
+
+#define RDF_TYPE_VOLUME "tracker#Volume"
+#define RDF_TYPE_UPNP "upnp#ContentDirectory"
+
+/**/
+
+typedef void (*tracker_grl_sparql_setter_cb_t) (TrackerSparqlCursor *cursor,
+ gint column,
+ GrlMedia *media,
+ GrlKeyID key);
+
+typedef struct {
+ GrlKeyID grl_key;
+ const gchar *sparql_key_name;
+ const gchar *sparql_key_name_canon;
+ const gchar *sparql_key_attr;
+ const gchar *sparql_key_attr_call;
+ const gchar *sparql_key_flavor;
+
+ tracker_grl_sparql_setter_cb_t set_value;
+} tracker_grl_sparql_t;
+
+extern GrlKeyID grl_metadata_key_tracker_urn;
+
+const GList *grl_tracker_supported_keys (GrlSource *source);
+
+gboolean grl_tracker_key_is_supported (const GrlKeyID key);
+
+void grl_tracker_setup_key_mappings (void);
+
+tracker_grl_sparql_t *grl_tracker_get_mapping_from_sparql (const gchar *key);
+
+GrlMedia *grl_tracker_build_grilo_media (const gchar *rdf_type,
+ GrlTypeFilter type_filter);
+
+gchar *grl_tracker_source_get_device_constraint (GrlTrackerSourcePriv *priv);
+
+gchar *grl_tracker_source_get_select_string (const GList *keys);
+
+gchar *grl_tracker_tracker_get_insert_string (GrlMedia *media,
+ const GList *keys);
+
+gchar *grl_tracker_get_delete_string (const GList *keys);
+
+gchar *grl_tracker_get_delete_conditional_string (const gchar *urn,
+ const GList *keys);
+
+gchar *grl_tracker_get_source_name (const gchar *rdf_type,
+ const gchar *uri,
+ const gchar *datasource,
+ const gchar *datasource_name);
+
+#endif /* _GRL_TRACKER_UTILS_H_ */
diff --git a/src/tracker3/grl-tracker.c b/src/tracker3/grl-tracker.c
new file mode 100644
index 0000000..652e77b
--- /dev/null
+++ b/src/tracker3/grl-tracker.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2011-2012 Igalia S.L.
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Juan A. Suarez Romero <jasuarez@igalia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <grilo.h>
+#include <string.h>
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "grl-tracker.h"
+#include "grl-tracker-source.h"
+#include "grl-tracker-source-api.h"
+#include "grl-tracker-source-notif.h"
+#include "grl-tracker-request-queue.h"
+#include "grl-tracker-utils.h"
+
+/* --------- Logging -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT tracker_general_log_domain
+GRL_LOG_DOMAIN_STATIC(tracker_general_log_domain);
+
+/* ------- Definitions ------- */
+
+#define TRACKER_FOLDER_CLASS_REQUEST \
+ "SELECT ?urn WHERE " \
+ "{ " \
+ "?urn a rdfs:Class . " \
+ "FILTER(fn:ends-with(?urn,\"nfo#Folder\")) " \
+ "}"
+
+#define TRACKER_UPNP_CLASS_REQUEST \
+ "SELECT ?urn WHERE " \
+ "{ " \
+ "?urn a rdfs:Class . " \
+ "FILTER(fn:ends-with(?urn,\"upnp#UPnPDataObject\")) " \
+ "}"
+
+#define TRACKER_NOTIFY_FOLDER_UPDATE \
+ "INSERT " \
+ "{ " \
+ "<%s> tracker:notify true " \
+ "}"
+
+/* --- Other --- */
+
+gboolean grl_tracker3_plugin_init (GrlRegistry *registry,
+ GrlPlugin *plugin,
+ GList *configs);
+
+/* ===================== Globals ================= */
+
+TrackerSparqlConnection *grl_tracker_connection = NULL;
+GrlPlugin *grl_tracker_plugin;
+GCancellable *grl_tracker_plugin_init_cancel = NULL;
+gboolean grl_tracker_upnp_present = FALSE;
+GrlTrackerQueue *grl_tracker_queue = NULL;
+
+/* tracker plugin config */
+gboolean grl_tracker_browse_filesystem = FALSE;
+gboolean grl_tracker_show_documents = FALSE;
+
+/* =================== Tracker Plugin =============== */
+
+static void
+init_sources (void)
+{
+ grl_tracker_setup_key_mappings ();
+
+ grl_tracker_queue = grl_tracker_queue_new ();
+
+ if (grl_tracker_connection != NULL) {
+ grl_tracker_notify_init (grl_tracker_connection);
+
+ grl_tracker_source_sources_init ();
+ }
+}
+
+static void
+tracker_update_folder_class_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ init_sources ();
+}
+
+static void
+tracker_get_folder_class_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GError *error = NULL;
+ TrackerSparqlCursor *cursor;
+
+ GRL_DEBUG ("%s", __FUNCTION__);
+
+ cursor = tracker_sparql_connection_query_finish (grl_tracker_connection,
+ result, &error);
+
+ if (error) {
+ GRL_INFO ("Could not execute sparql query for folder class: %s",
+ error->message);
+ g_error_free (error);
+ }
+
+ if (!cursor) {
+ init_sources ();
+ return;
+ }
+
+ if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
+ gchar *update = g_strdup_printf (TRACKER_NOTIFY_FOLDER_UPDATE,
+ tracker_sparql_cursor_get_string (cursor,
+ 0,
+ NULL));
+
+ GRL_DEBUG ("\tupdate query: '%s'", update);
+
+ tracker_sparql_connection_update_async (grl_tracker_connection,
+ update,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ tracker_update_folder_class_cb,
+ NULL);
+
+ g_free (update);
+ }
+
+ g_object_unref (cursor);
+}
+
+static void
+tracker_get_upnp_class_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GError *error = NULL;
+ TrackerSparqlCursor *cursor;
+
+ GRL_DEBUG ("%s", G_STRFUNC);
+
+ cursor = tracker_sparql_connection_query_finish (grl_tracker_connection,
+ result, &error);
+ if (error) {
+ GRL_INFO ("Could not execute sparql query for upnp class: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
+ GRL_DEBUG ("\tUPnP ontology present");
+ grl_tracker_upnp_present = TRUE;
+ }
+ }
+
+ g_clear_object (&cursor);
+
+ if (grl_tracker_browse_filesystem)
+ tracker_sparql_connection_query_async (grl_tracker_connection,
+ TRACKER_FOLDER_CLASS_REQUEST,
+ grl_tracker_plugin_init_cancel,
+ tracker_get_folder_class_cb,
+ NULL);
+ else
+ init_sources ();
+}
+
+static void
+tracker_new_connection_cb (GObject *object,
+ GAsyncResult *res,
+ GrlPlugin *plugin)
+{
+ GError *error = NULL;
+
+ GRL_DEBUG ("%s", __FUNCTION__);
+
+ grl_tracker_connection = tracker_sparql_connection_new_finish (res, &error);
+
+ if (error) {
+ GRL_INFO ("Could not get connection to Tracker: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ GRL_DEBUG ("\trequest : '%s'", TRACKER_UPNP_CLASS_REQUEST);
+
+ tracker_sparql_connection_query_async (grl_tracker_connection,
+ TRACKER_UPNP_CLASS_REQUEST,
+ grl_tracker_plugin_init_cancel,
+ tracker_get_upnp_class_cb,
+ NULL);
+}
+
+gboolean
+grl_tracker3_plugin_init (GrlRegistry *registry,
+ GrlPlugin *plugin,
+ GList *configs)
+{
+ GrlConfig *config;
+ gint config_count;
+ GFile *ontology;
+
+ GRL_LOG_DOMAIN_INIT (tracker_general_log_domain, "tracker3-general");
+
+ /* Initialize i18n */
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+ grl_tracker_source_init_requests ();
+
+ grl_tracker_plugin = plugin;
+
+ if (!configs) {
+ GRL_INFO ("\tConfiguration not provided! Using default configuration.");
+ } else {
+ config_count = g_list_length (configs);
+ if (config_count > 1) {
+ GRL_INFO ("\tProvided %i configs, but will only use one", config_count);
+ }
+
+ config = GRL_CONFIG (configs->data);
+
+ grl_tracker_browse_filesystem =
+ grl_config_get_boolean (config, "browse-filesystem");
+ grl_tracker_show_documents =
+ grl_config_get_boolean (config, "show-documents");
+ }
+
+ grl_tracker_plugin_init_cancel = g_cancellable_new ();
+ ontology = tracker_sparql_get_ontology_nepomuk ();
+ tracker_sparql_connection_new_async (TRACKER_SPARQL_CONNECTION_FLAGS_NONE,
+ NULL,
+ ontology,
+ grl_tracker_plugin_init_cancel,
+ (GAsyncReadyCallback) tracker_new_connection_cb,
+ plugin);
+ g_object_unref (ontology);
+
+ return TRUE;
+}
+
+static void
+grl_tracker3_plugin_deinit (GrlPlugin *plugin)
+{
+ g_cancellable_cancel (grl_tracker_plugin_init_cancel);
+ g_clear_object (&grl_tracker_plugin_init_cancel);
+ g_clear_object (&grl_tracker_connection);
+}
+
+static void
+grl_tracker3_plugin_register_keys (GrlRegistry *registry,
+ GrlPlugin *plugin)
+{
+ grl_registry_register_metadata_key (grl_registry_get_default (),
+ g_param_spec_string ("tracker-category",
+ "Tracker category",
+ "Category a media belongs to",
+ NULL,
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_READWRITE),
+ GRL_METADATA_KEY_INVALID,
+ NULL);
+ grl_registry_register_metadata_key (grl_registry_get_default (),
+ g_param_spec_string ("gibest-hash",
+ "Gibest hash",
+ "Gibest hash of the video file",
+ NULL,
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_READWRITE),
+ GRL_METADATA_KEY_INVALID,
+ NULL);
+ grl_registry_register_metadata_key (grl_registry_get_default (),
+ g_param_spec_string ("tracker-urn",
+ "Tracker URN",
+ "Universal resource number in Tracker's store",
+ NULL,
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_READWRITE),
+ GRL_METADATA_KEY_INVALID,
+ NULL);
+}
+
+GRL_PLUGIN_DEFINE (GRL_MAJOR,
+ GRL_MINOR,
+ GRL_TRACKER_PLUGIN_ID,
+ "Tracker3",
+ "A plugin for searching multimedia content using Tracker Miners 3.x",
+ "Igalia S.L.",
+ VERSION,
+ "LGPL",
+ "http://www.igalia.com",
+ grl_tracker3_plugin_init,
+ grl_tracker3_plugin_deinit,
+ grl_tracker3_plugin_register_keys);
diff --git a/src/tracker3/grl-tracker.h b/src/tracker3/grl-tracker.h
new file mode 100644
index 0000000..847ecd2
--- /dev/null
+++ b/src/tracker3/grl-tracker.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * Contact: Iago Toral Quiroga <itoral@igalia.com>
+ *
+ * Authors: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_TRACKER_H_
+#define _GRL_TRACKER_H_
+
+#include "grl-tracker-request-queue.h"
+
+/* ---- Plugin information --- */
+
+#define GRL_TRACKER_PLUGIN_ID TRACKER3_PLUGIN_ID
+
+extern GrlTrackerQueue *grl_tracker_queue;
+extern TrackerSparqlConnection *grl_tracker_connection;
+
+#endif /* _GRL_TRACKER_H_ */
diff --git a/src/tracker3/meson.build b/src/tracker3/meson.build
new file mode 100644
index 0000000..4ce507c
--- /dev/null
+++ b/src/tracker3/meson.build
@@ -0,0 +1,37 @@
+#
+# meson.build
+#
+# Author: Juan A. Suarez Romero <jasuarez@igalia.com>
+#
+# Copyright (C) 2016 Igalia S.L. All rights reserved.
+
+tracker_sources = [
+ 'grl-tracker-request-queue.c',
+ 'grl-tracker-request-queue.h',
+ 'grl-tracker-source-api.c',
+ 'grl-tracker-source-api.h',
+ 'grl-tracker-source-cache.c',
+ 'grl-tracker-source-cache.h',
+ 'grl-tracker-source-notif.c',
+ 'grl-tracker-source-notif.h',
+ 'grl-tracker-source-priv.h',
+ 'grl-tracker-source.c',
+ 'grl-tracker-source.h',
+ 'grl-tracker-utils.c',
+ 'grl-tracker-utils.h',
+ 'grl-tracker.c',
+ 'grl-tracker.h',
+]
+
+configure_file(output: 'config.h',
+ configuration: cdata)
+
+shared_library('grltracker3',
+ sources: tracker_sources,
+ install: true,
+ install_dir: pluginsdir,
+ dependencies: must_deps + plugins[tracker3_idx][REQ_DEPS] + plugins[tracker3_idx][OPT_DEPS],
+ c_args: [
+ '-DG_LOG_DOMAIN="GrlTracker3"',
+ '-DHAVE_CONFIG_H',
+ ])