[rygel] tracker: Initial port to 0.7 APIs



commit f991f5c021b14010a8a37135066f0c6dce307cab
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Sat Nov 21 01:31:59 2009 +0200

    tracker: Initial port to 0.7 APIs

 src/plugins/tracker/Makefile.am                    |    1 +
 src/plugins/tracker/rygel-tracker-image-item.vala  |   21 +-
 src/plugins/tracker/rygel-tracker-interfaces.vala  |   45 +---
 src/plugins/tracker/rygel-tracker-item.vala        |   74 ++----
 src/plugins/tracker/rygel-tracker-keywords.vala    |   66 +-----
 .../tracker/rygel-tracker-metadata-values.vala     |  101 +++++---
 src/plugins/tracker/rygel-tracker-music-item.vala  |   22 +-
 .../tracker/rygel-tracker-plugin-factory.vala      |   15 +-
 src/plugins/tracker/rygel-tracker-query.vala       |  168 ++++++++++++
 .../tracker/rygel-tracker-root-container.vala      |   27 ++-
 .../tracker/rygel-tracker-search-container.vala    |  277 +++++++++-----------
 src/plugins/tracker/rygel-tracker-video-item.vala  |   23 +-
 12 files changed, 453 insertions(+), 387 deletions(-)
---
diff --git a/src/plugins/tracker/Makefile.am b/src/plugins/tracker/Makefile.am
index 2418883..8ed89e8 100644
--- a/src/plugins/tracker/Makefile.am
+++ b/src/plugins/tracker/Makefile.am
@@ -15,6 +15,7 @@ librygel_media_tracker_la_SOURCES = \
 				    rygel-tracker-metadata-values.vala \
 				    rygel-tracker-keywords.vala \
 				    rygel-tracker-search-container.vala \
+				    rygel-tracker-query.vala \
 				    rygel-tracker-item.vala \
 				    rygel-tracker-video-item.vala \
 				    rygel-tracker-music-item.vala \
diff --git a/src/plugins/tracker/rygel-tracker-image-item.vala b/src/plugins/tracker/rygel-tracker-image-item.vala
index 3abaf55..a827b46 100644
--- a/src/plugins/tracker/rygel-tracker-image-item.vala
+++ b/src/plugins/tracker/rygel-tracker-image-item.vala
@@ -28,7 +28,7 @@ using DBus;
  * Represents Tracker image item.
  */
 public class Rygel.TrackerImageItem : Rygel.TrackerItem {
-    public const string SERVICE = "Images";
+    public const string CATEGORY = "nmm:Photo";
 
     public TrackerImageItem (string                 id,
                              string                 path,
@@ -37,24 +37,21 @@ public class Rygel.TrackerImageItem : Rygel.TrackerItem {
                              throws GLib.Error {
         base (id, path, parent, MediaItem.IMAGE_CLASS, metadata);
 
-        if (metadata[Metadata.IMAGE_TITLE] != "")
-            this.title = metadata[Metadata.IMAGE_TITLE];
+        if (metadata[Metadata.TITLE] != "")
+            this.title = metadata[Metadata.TITLE];
         else
             /* If title wasn't provided, use filename instead */
             this.title = metadata[Metadata.FILE_NAME];
 
-        if (metadata[Metadata.IMAGE_WIDTH] != "")
-            this.width = metadata[Metadata.IMAGE_WIDTH].to_int ();
+        if (metadata[Metadata.WIDTH] != "")
+            this.width = metadata[Metadata.WIDTH].to_int ();
 
-        if (metadata[Metadata.IMAGE_HEIGHT] != "")
-            this.height = metadata[Metadata.IMAGE_HEIGHT].to_int ();
+        if (metadata[Metadata.HEIGHT] != "")
+            this.height = metadata[Metadata.HEIGHT].to_int ();
 
-        if (metadata[Metadata.IMAGE_DATE] != "") {
-            this.date = seconds_to_iso8601 (metadata[Metadata.IMAGE_DATE]);
+        if (metadata[Metadata.DATE] != "") {
+            this.date = seconds_to_iso8601 (metadata[Metadata.DATE]);
         }
-
-        this.author = metadata[Metadata.CREATOR];
-        this.album = metadata[Metadata.IMAGE_ALBUM];
     }
 }
 
diff --git a/src/plugins/tracker/rygel-tracker-interfaces.vala b/src/plugins/tracker/rygel-tracker-interfaces.vala
index 98194f1..7b9bb3c 100644
--- a/src/plugins/tracker/rygel-tracker-interfaces.vala
+++ b/src/plugins/tracker/rygel-tracker-interfaces.vala
@@ -23,44 +23,13 @@
 
 using DBus;
 
-[DBus (name = "org.freedesktop.Tracker")]
-public interface Rygel.TrackerIface : DBus.Object {
-    public abstract async int get_version () throws DBus.Error;
+[DBus (name = "org.freedesktop.Tracker1.Statistics")]
+public interface Rygel.TrackerStatsIface : DBus.Object {
+    public abstract async string[,] get_statistics () throws DBus.Error;
 }
 
-[DBus (name = "org.freedesktop.Tracker.Keywords")]
-public interface Rygel.TrackerKeywordsIface : DBus.Object {
-    public abstract async string[,] get_list (string service) throws DBus.Error;
-}
-
-[DBus (name = "org.freedesktop.Tracker.Metadata")]
-public interface Rygel.TrackerMetadataIface: DBus.Object {
-    public abstract async string[,] get_unique_values (string   service,
-                                                       string[] meta_types,
-                                                       string   query,
-                                                       bool     descending,
-                                                       int      offset,
-                                                       int      max_hits)
-                                                       throws DBus.Error;
-
-    public abstract async string[] @get (string   service_type,
-                                         string   uri,
-                                         string[] keys)
-                                         throws DBus.Error;
-}
-
-[DBus (name = "org.freedesktop.Tracker.Search")]
-public interface Rygel.TrackerSearchIface: DBus.Object {
-    public abstract async string[,] query (int live_query_id,
-                                           string   service,
-                                           string[] fields,
-                                           string   search_text,
-                                           string[] keywords,
-                                           string   query_condition,
-                                           bool     sort_by_service,
-                                           string[] sort_fields,
-                                           bool     sort_descending,
-                                           int      offset,
-                                           int      max_hits)
-                                           throws DBus.Error;
+[DBus (name = "org.freedesktop.Tracker1.Resources")]
+public interface Rygel.TrackerResourcesIface: DBus.Object {
+    public abstract async string[,] sparql_query (string query)
+                                                  throws DBus.Error;
 }
diff --git a/src/plugins/tracker/rygel-tracker-item.vala b/src/plugins/tracker/rygel-tracker-item.vala
index 8a76e6f..7bc2b26 100644
--- a/src/plugins/tracker/rygel-tracker-item.vala
+++ b/src/plugins/tracker/rygel-tracker-item.vala
@@ -30,33 +30,22 @@ using DBus;
 public abstract class Rygel.TrackerItem : Rygel.MediaItem {
     protected enum Metadata {
         FILE_NAME,
+        TITLE,
         MIME,
         SIZE,
         DATE,
 
-        // Image
-        IMAGE_TITLE,
-        IMAGE_WIDTH,
-        IMAGE_HEIGHT,
-        IMAGE_ALBUM,
-        IMAGE_DATE,
-        CREATOR,
+        // Image and Video
+        HEIGHT,
+        WIDTH,
+
+        // Audio and Video
+        DURATION,
 
         // Audio
-        AUDIO_TITLE,
-        AUDIO_DURATION,
         AUDIO_ALBUM,
-        ARTIST,
-        TRACK_NUM,
-        RELEASE,
-        DATE_ADDED,
-
-        // Video
-        VIDEO_TITLE,
-        VIDEO_WIDTH,
-        VIDEO_HEIGHT,
-        VIDEO_DURATION,
-        AUTHOR,
+        AUDIO_ARTIST,
+        AUDIO_TRACK_NUM,
 
         LAST_KEY
     }
@@ -86,38 +75,31 @@ public abstract class Rygel.TrackerItem : Rygel.MediaItem {
 
     public static string[] get_metadata_keys () {
         string[] keys = new string[Metadata.LAST_KEY];
-        keys[Metadata.FILE_NAME] = "File:Name";
-        keys[Metadata.MIME] = "File:Mime";
-        keys[Metadata.SIZE] = "File:Size";
-        keys[Metadata.DATE] = "DC:Date";
-
-        // Image metadata
-        keys[Metadata.IMAGE_TITLE] = "Image:Title";
-        keys[Metadata.CREATOR] = "Image:Creator";
-        keys[Metadata.IMAGE_WIDTH] = "Image:Width";
-        keys[Metadata.IMAGE_HEIGHT] = "Image:Height";
-        keys[Metadata.IMAGE_ALBUM] = "Image:Album";
-        keys[Metadata.IMAGE_DATE] = "Image:Date";
+        keys[Metadata.FILE_NAME] = "nfo:fileName";
+        keys[Metadata.TITLE] = "nie:title";
+        keys[Metadata.MIME] = "nie:mimeType";
+        keys[Metadata.SIZE] = "nfo:fileSize";
+        keys[Metadata.DATE] = "dc:date";
+
+        // Image and Video metadata
+        keys[Metadata.WIDTH] = "nfo:width";
+        keys[Metadata.HEIGHT] = "nfo:height";
+
+        // Audio and Video metadata
+        keys[Metadata.DURATION] = "nmm:length";
 
         // Audio metadata
-        keys[Metadata.AUDIO_TITLE] = "Audio:Title";
-        keys[Metadata.AUDIO_DURATION] = "Audio:Duration";
-        keys[Metadata.ARTIST] = "Audio:Artist";
-        keys[Metadata.AUDIO_ALBUM] = "Audio:Album";
-        keys[Metadata.TRACK_NUM] = "Audio:TrackNo";
-        keys[Metadata.RELEASE] = "Audio:ReleaseDate";
-        keys[Metadata.DATE_ADDED] = "Audio:DateAdded";
-
-        // Video metadata
-        keys[Metadata.VIDEO_DURATION] = "Video:Duration";
-        keys[Metadata.VIDEO_TITLE] = "Video:Title";
-        keys[Metadata.AUTHOR] = "Video:Author";
-        keys[Metadata.VIDEO_WIDTH] = "Video:Width";
-        keys[Metadata.VIDEO_HEIGHT] = "Video:Height";
+        keys[Metadata.AUDIO_ARTIST] = "nmm:performer";
+        keys[Metadata.AUDIO_ALBUM] = "nmm:musicAlbum";
+        keys[Metadata.AUDIO_TRACK_NUM] = "nmm:trackNumber";
 
         return keys;
     }
 
+    public int get_num_metadata_keys () {
+        return Metadata.LAST_KEY;
+    }
+
     protected string seconds_to_iso8601 (string seconds) {
         string date;
 
diff --git a/src/plugins/tracker/rygel-tracker-keywords.vala b/src/plugins/tracker/rygel-tracker-keywords.vala
index 6bbf2a9..84f73e1 100644
--- a/src/plugins/tracker/rygel-tracker-keywords.vala
+++ b/src/plugins/tracker/rygel-tracker-keywords.vala
@@ -26,73 +26,17 @@ using DBus;
 using Gee;
 
 /**
- * Container listing all available keywords (tags) in Tracker DB.
+ * Container listing all available photo tags in Tracker DB.
  */
-public class Rygel.TrackerKeywords : Rygel.SimpleContainer {
+public class Rygel.TrackerKeywords : Rygel.TrackerMetadataValues {
     /* class-wide constants */
-    private const string TRACKER_SERVICE = "org.freedesktop.Tracker";
-    private const string KEYWORDS_PATH = "/org/freedesktop/Tracker/Keywords";
-
-    private const string SERVICE = "Files";
     private const string TITLE = "Tags";
-
-    public TrackerKeywordsIface keywords;
+    private const string CATEGORY = "nmm:Photo";
+    private const string[] KEY_CHAIN = { "nao:hasTag", "nao:prefLabel", null };
 
     public TrackerKeywords (string         id,
                             MediaContainer parent) {
-        base (id, parent, TITLE);
-
-        try {
-            this.create_proxies ();
-        } catch (DBus.Error error) {
-            critical ("Failed to create to Session bus: %s\n",
-                      error.message);
-
-            return;
-        }
-
-        this.fetch_keywords.begin ();
-    }
-
-    private async void fetch_keywords () {
-        string[,] keywords_list;
-
-        try {
-            /* FIXME: We need to hook to some tracker signals to keep
-             *        this field up2date at all times
-             */
-            keywords_list = yield this.keywords.get_list (SERVICE);
-        } catch (DBus.Error error) {
-            critical ("error getting all keywords: %s", error.message);
-
-            return;
-        }
-
-        /* Iterate through all the values */
-        for (uint i = 0; i < keywords_list.length[0]; i++) {
-            string keyword = keywords_list[i, 0];
-
-            var keywords = new string[] { keyword };
-
-            var container = new TrackerSearchContainer (keyword,
-                                                        this,
-                                                        keyword,
-                                                        SERVICE,
-                                                        "",
-                                                        keywords);
-
-            this.add_child (container);
-        }
-
-        this.updated ();
-    }
-
-    private void create_proxies () throws DBus.Error {
-        DBus.Connection connection = DBus.Bus.get (DBus.BusType.SESSION);
-
-        this.keywords = connection.get_object (TRACKER_SERVICE,
-                                               KEYWORDS_PATH)
-                                               as TrackerKeywordsIface;
+        base (id, parent, TITLE, CATEGORY, KEY_CHAIN);
     }
 }
 
diff --git a/src/plugins/tracker/rygel-tracker-metadata-values.vala b/src/plugins/tracker/rygel-tracker-metadata-values.vala
index a8fceb2..1b3bb0b 100644
--- a/src/plugins/tracker/rygel-tracker-metadata-values.vala
+++ b/src/plugins/tracker/rygel-tracker-metadata-values.vala
@@ -30,27 +30,28 @@ using Gee;
  */
 public class Rygel.TrackerMetadataValues : Rygel.SimpleContainer {
     /* class-wide constants */
-    private const string TRACKER_SERVICE = "org.freedesktop.Tracker";
-    private const string METADATA_PATH = "/org/freedesktop/Tracker/Metadata";
+    private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
+    private const string RESOURCES_PATH = "/org/freedesktop/Tracker1/Resources";
+    private const string ITEM_VARIABLE = "?item";
 
-    private const string SERVICE = "Files";
-    private const string QUERY_CONDITION =
-                                        "<rdfq:equals>\n" +
-                                            "<rdfq:Property name=\"%s\" />\n" +
-                                            "<rdf:String>%s</rdf:String>\n" +
-                                        "</rdfq:equals>\n";
+    private string category;
 
-    public TrackerMetadataIface metadata;
+    // In tracker 0.7, we might don't get values of keys in place so you need a
+    // chain of keys to reach to final destination. For instances:
+    // nmm:Performer -> nmm:artistName
+    public string[] key_chain;
 
-    public string key;
+    private TrackerResourcesIface resources;
 
-    public TrackerMetadataValues (string         key,
-                                  string         id,
+    public TrackerMetadataValues (string         id,
                                   MediaContainer parent,
-                                  string         title) {
+                                  string         title,
+                                  string         category,
+                                  string[]       key_chain) {
         base (id, parent, title);
 
-        this.key = key;
+        this.category = category;
+        this.key_chain = key_chain;
 
         try {
             this.create_proxies ();
@@ -65,44 +66,78 @@ public class Rygel.TrackerMetadataValues : Rygel.SimpleContainer {
     }
 
     private async void fetch_metadata_values () {
-        string[,] values;
+        int i;
+        var mandatory = new ArrayList<TrackerQueryTriplet> ();
+
+        // All variables used in the query
+        var num_keys = this.key_chain.length - 1;
+        var variables = new string[num_keys];
+        for (i = 0; i < num_keys; i++) {
+            variables[i] = "?" + key_chain[i].replace (":", "_");
+
+            string subject;
+            if (i == 0) {
+                subject = null;
+            } else {
+                subject = variables[i - 1];
+            }
 
-        try {
-            var keys = new string[] { this.key };
+            mandatory.add (new TrackerQueryTriplet (subject,
+                                                    this.key_chain[i],
+                                                    variables[i],
+                                                    false));
+        }
+
+        mandatory.insert (0, new TrackerQueryTriplet (ITEM_VARIABLE,
+                                                      "a",
+                                                      this.category,
+                                                      false));
 
+        // Variables to select from query
+        var selected = new ArrayList<string> ();
+        // Last variable is the only thing we are interested in the result
+        var last_variable = variables[num_keys - 1];
+        selected.add ("DISTINCT " + last_variable);
+
+        var query = new TrackerQuery (selected,
+                                      mandatory,
+                                      null,
+                                      null,
+                                      last_variable);
+
+        string[,] values;
+        try {
             /* FIXME: We need to hook to some tracker signals to keep
              *        this field up2date at all times
              */
-            values = yield this.metadata.get_unique_values (SERVICE,
-                                                            keys,
-                                                            "",
-                                                            false,
-                                                            0,
-                                                            -1);
+            debug ("Executing SPARQL query: %s", query.to_string ());
+            values = yield this.resources.sparql_query (query.to_string ());
         } catch (DBus.Error error) {
             critical ("error getting all values for '%s': %s",
-                      this.key,
+                      string.joinv (" -> ", this.key_chain),
                       error.message);
 
             return;
         }
 
         /* Iterate through all the values */
-        for (uint i = 0; i < values.length[0]; i++) {
+        for (i = 0; i < values.length[0]; i++) {
             string value = values[i, 0];
 
             if (value == "") {
                 continue;
             }
 
-            var query_condition = QUERY_CONDITION.printf (
-                                        this.key,
-                                        Markup.escape_text (value));
+            // The child container can use the same mandatory triplets we used
+            // in our query except that last value is now fixed
+            mandatory.last ().obj = "\"" + value + "\"";
+
             var container = new TrackerSearchContainer (value,
                                                         this,
                                                         value,
-                                                        SERVICE,
-                                                        query_condition);
+                                                        this.category,
+                                                        mandatory,
+                                                        null);
 
             this.add_child (container);
         }
@@ -113,9 +148,9 @@ public class Rygel.TrackerMetadataValues : Rygel.SimpleContainer {
     private void create_proxies () throws DBus.Error {
         DBus.Connection connection = DBus.Bus.get (DBus.BusType.SESSION);
 
-        this.metadata = connection.get_object (TRACKER_SERVICE,
-                                               METADATA_PATH)
-                                               as TrackerMetadataIface;
+        this.resources = connection.get_object (TRACKER_SERVICE,
+                                                RESOURCES_PATH)
+                                                as TrackerResourcesIface;
     }
 }
 
diff --git a/src/plugins/tracker/rygel-tracker-music-item.vala b/src/plugins/tracker/rygel-tracker-music-item.vala
index d7903ad..681bffd 100644
--- a/src/plugins/tracker/rygel-tracker-music-item.vala
+++ b/src/plugins/tracker/rygel-tracker-music-item.vala
@@ -28,7 +28,7 @@ using DBus;
  * Represents Tracker music item.
  */
 public class Rygel.TrackerMusicItem : Rygel.TrackerItem {
-    public const string SERVICE = "Music";
+    public const string CATEGORY = "nmm:MusicPiece";
 
     public TrackerMusicItem (string                 id,
                              string                 path,
@@ -37,25 +37,23 @@ public class Rygel.TrackerMusicItem : Rygel.TrackerItem {
                              throws GLib.Error {
         base (id, path, parent, MediaItem.MUSIC_CLASS, metadata);
 
-        if (metadata[Metadata.AUDIO_TITLE] != "")
-            this.title = metadata[Metadata.AUDIO_TITLE];
+        if (metadata[Metadata.TITLE] != "")
+            this.title = metadata[Metadata.TITLE];
         else
             /* If title wasn't provided, use filename instead */
             this.title = metadata[Metadata.FILE_NAME];
 
-        if (metadata[Metadata.AUDIO_DURATION] != "")
-            this.duration = metadata[Metadata.AUDIO_DURATION].to_int ();
+        if (metadata[Metadata.DURATION] != "")
+            this.duration = metadata[Metadata.DURATION].to_int ();
 
-        if (metadata[Metadata.TRACK_NUM] != "")
-            this.track_number = metadata[Metadata.TRACK_NUM].to_int ();
+        if (metadata[Metadata.AUDIO_TRACK_NUM] != "")
+            this.track_number = metadata[Metadata.AUDIO_TRACK_NUM].to_int ();
 
-        if (metadata[Metadata.RELEASE] != "") {
-            this.date = seconds_to_iso8601 (metadata[Metadata.RELEASE]);
-        } else {
-            this.date = seconds_to_iso8601 (metadata[Metadata.DATE_ADDED]);
+        if (metadata[Metadata.DATE] != "") {
+            this.date = seconds_to_iso8601 (metadata[Metadata.DATE]);
         }
 
-        this.author = metadata[Metadata.ARTIST];
+        this.author = metadata[Metadata.AUDIO_ARTIST];
         this.album = metadata[Metadata.AUDIO_ALBUM];
     }
 }
diff --git a/src/plugins/tracker/rygel-tracker-plugin-factory.vala b/src/plugins/tracker/rygel-tracker-plugin-factory.vala
index f057752..5558fdc 100644
--- a/src/plugins/tracker/rygel-tracker-plugin-factory.vala
+++ b/src/plugins/tracker/rygel-tracker-plugin-factory.vala
@@ -40,21 +40,22 @@ public void module_init (PluginLoader loader) {
 }
 
 public class TrackerPluginFactory {
-    private const string TRACKER_SERVICE = "org.freedesktop.Tracker";
-    private const string TRACKER_OBJECT = "/org/freedesktop/Tracker";
+    private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
+    private const string STATISTICS_OBJECT =
+                                        "/org/freedesktop/Tracker1/Statistics";
 
-    TrackerIface tracker;
+    TrackerStatsIface stats;
     PluginLoader loader;
 
     public TrackerPluginFactory (PluginLoader loader) throws DBus.Error {
         var connection = DBus.Bus.get (DBus.BusType.SESSION);
 
-        this.tracker = connection.get_object (TRACKER_SERVICE,
-                                              TRACKER_OBJECT)
-                                              as TrackerIface;
+        this.stats = connection.get_object (TRACKER_SERVICE,
+                                            STATISTICS_OBJECT)
+                                            as TrackerStatsIface;
         this.loader = loader;
 
-        this.tracker.get_version ();
+        this.stats.get_statistics ();
 
         this.loader.add_plugin (new TrackerPlugin ());
     }
diff --git a/src/plugins/tracker/rygel-tracker-query.vala b/src/plugins/tracker/rygel-tracker-query.vala
new file mode 100644
index 0000000..35709cb
--- /dev/null
+++ b/src/plugins/tracker/rygel-tracker-query.vala
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali <zeenix gmail com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Gee;
+
+/**
+ * Represents Tracker SPARQL query
+ */
+public class Rygel.TrackerQuery {
+    public ArrayList<TrackerQueryTriplet> mandatory;
+    public ArrayList<TrackerQueryTriplet> optional;
+
+    public ArrayList<string> variables;
+    public ArrayList<string> filters;
+
+    public string order_by;
+    public int offset;
+    public int max_count;
+
+    public TrackerQuery (ArrayList<string>               variables,
+                         ArrayList<TrackerQueryTriplet>  mandatory,
+                         ArrayList<TrackerQueryTriplet>? optional,
+                         ArrayList<string>?              filters,
+                         string?                         order_by = null,
+                         int                             offset = 0,
+                         int                             max_count = -1) {
+        this.variables = variables;
+        this.mandatory = mandatory;
+
+        if (optional != null) {
+            this.optional = optional;
+        } else {
+            this.optional = new ArrayList<TrackerQueryTriplet> ();
+        }
+
+        if (filters != null) {
+            this.filters = filters;
+        } else {
+            this.filters = new ArrayList<string> ();
+        }
+
+        this.order_by = order_by;
+
+        this.offset = offset;
+        this.max_count = max_count;
+    }
+
+    public TrackerQuery.from_template (TrackerQuery template) {
+        this (template.variables,
+              template.mandatory,
+              template.optional,
+              template.filters,
+              template.order_by,
+              template.offset,
+              template.max_count);
+    }
+
+    public string to_string () {
+        string query = "SELECT";
+
+        foreach (var variable in this.variables) {
+            query += " " + variable;
+        }
+
+        query += " WHERE { " +
+                 this.serialize_triplets (this.mandatory) +
+                 " . " +
+                 this.serialize_triplets (this.optional);
+
+        foreach (var filter in this.filters) {
+            query += " " + filter;
+        }
+
+        query += " }";
+
+        if (this.order_by != null) {
+            query += " ORDER BY " + order_by;
+        }
+
+        query += " OFFSET " + this.offset.to_string ();
+
+        if (this.max_count != -1) {
+            query += " LIMIT " + this.max_count.to_string ();
+        }
+
+        return query;
+    }
+
+    private string serialize_triplets (Gee.List<TrackerQueryTriplet> triplets) {
+        string str = "";
+
+        for (int i = 0; i < triplets.size; i++) {
+            str += triplets[i].to_string ();
+
+            if (i < triplets.size - 1) {
+                if (triplets[i + 1].subject != null) {
+                    str += " . ";
+                } else {
+                    // This implies that next triplet shares the subject with
+                    // this one so we need to end this one with a semi-colon.
+                    str += " ; ";
+                }
+            }
+        }
+
+        return str;
+    }
+}
+
+/**
+ * Represents SPARQL Triplet
+ */
+public class Rygel.TrackerQueryTriplet {
+    public string subject;
+    public string predicate;
+    public string obj;
+
+    public bool optional;
+
+    public TrackerQueryTriplet (string? subject,
+                                string  predicate,
+                                string  obj,
+                                bool    optional = true) {
+        this.subject = subject;
+        this.predicate = predicate;
+        this.obj = obj;
+        this.optional = optional;
+    }
+
+    public string to_string () {
+        string str = "";
+
+        if (this.optional) {
+            str += "OPTIONAL {";
+        }
+
+        if (this.subject != null) {
+            str += " " + subject;
+        }
+
+        str += " " + predicate + " " + obj;
+
+        if (this.optional) {
+            str += " }";
+        }
+
+        return str;
+    }
+}
diff --git a/src/plugins/tracker/rygel-tracker-root-container.vala b/src/plugins/tracker/rygel-tracker-root-container.vala
index de7ac8e..23c9176 100644
--- a/src/plugins/tracker/rygel-tracker-root-container.vala
+++ b/src/plugins/tracker/rygel-tracker-root-container.vala
@@ -37,25 +37,34 @@ public class Rygel.TrackerRootContainer : Rygel.SimpleContainer {
                                         "16",
                                         this,
                                         "Pictures",
-                                        TrackerImageItem.SERVICE));
+                                        TrackerImageItem.CATEGORY));
         this.add_child (new TrackerSearchContainer (
                                         "14",
                                         this,
                                         "Music",
-                                        TrackerMusicItem.SERVICE));
+                                        TrackerMusicItem.CATEGORY));
         this.add_child (new TrackerSearchContainer (
                                         "15",
                                         this,
                                         "Videos",
-                                        TrackerVideoItem.SERVICE));
-        this.add_child (new TrackerMetadataValues ("Audio:Artist",
-                                                   "17",
+                                        TrackerVideoItem.CATEGORY));
+
+        var key_chain = new string[] { "nmm:performer",
+                                       "nmm:artistName",
+                                       null };
+        this.add_child (new TrackerMetadataValues ("17",
                                                    this,
-                                                   "Artists"));
-        this.add_child (new TrackerMetadataValues ("Audio:Album",
-                                                   "18",
+                                                   "Artists",
+                                                   TrackerMusicItem.CATEGORY,
+                                                   key_chain));
+
+        key_chain = new string[] { "nmm:musicAlbum", "nmm:albumTitle", null };
+        this.add_child (new TrackerMetadataValues ("18",
                                                    this,
-                                                   "Albums"));
+                                                   "Albums",
+                                                   TrackerMusicItem.CATEGORY,
+                                                   key_chain));
+
         this.add_child (new TrackerKeywords ("19", this));
     }
 }
diff --git a/src/plugins/tracker/rygel-tracker-search-container.vala b/src/plugins/tracker/rygel-tracker-search-container.vala
index 7221f3d..45799b4 100644
--- a/src/plugins/tracker/rygel-tracker-search-container.vala
+++ b/src/plugins/tracker/rygel-tracker-search-container.vala
@@ -30,30 +30,53 @@ using Gee;
  */
 public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
     /* class-wide constants */
-    private const string TRACKER_SERVICE = "org.freedesktop.Tracker";
-    private const string TRACKER_PATH = "/org/freedesktop/Tracker";
-    private const string SEARCH_PATH = "/org/freedesktop/Tracker/Search";
-    private const string METADATA_PATH = "/org/freedesktop/Tracker/Metadata";
+    private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
+    private const string RESOURCES_PATH = "/org/freedesktop/Tracker1/Resources";
 
-    public TrackerSearchIface search_proxy;
+    private const string ITEM_VARIABLE = "?item";
 
-    public string service;
+    public TrackerQuery query;
+    public string category;
 
-    public string query_condition;
+    private TrackerResourcesIface resources;
 
-    public string[] keywords;
-
-    public TrackerSearchContainer (string         id,
-                                   MediaContainer parent,
-                                   string         title,
-                                   string         service,
-                                   string         query_condition = "",
-                                   string[]       keywords = new string[0]) {
+    public TrackerSearchContainer (string                         id,
+                                   MediaContainer                 parent,
+                                   string                         title,
+                                   string                         category,
+                                   ArrayList<TrackerQueryTriplet> mandatory =
+                                        new ArrayList<TrackerQueryTriplet> (),
+                                   ArrayList<string>?             filters =
+                                        null) {
         base (id, parent, title, 0);
 
-        this.service = service;
-        this.keywords = keywords;
-        this.query_condition = query_condition;
+        this.category = category;
+
+        var variables = new ArrayList<string> ();
+        variables.add (ITEM_VARIABLE);
+
+        mandatory.add (new TrackerQueryTriplet (ITEM_VARIABLE,
+                                                "a",
+                                                category,
+                                                false));
+
+        var optional = new ArrayList<TrackerQueryTriplet> ();
+        foreach (var key in TrackerItem.get_metadata_keys ()) {
+            var variable = "?" + key.replace (":", "_");
+
+            variables.add (variable);
+
+            var triplet = new TrackerQueryTriplet (ITEM_VARIABLE,
+                                                   key,
+                                                   variable);
+            optional.add (triplet);
+        }
+
+        this.query = new TrackerQuery (variables,
+                                       mandatory,
+                                       optional,
+                                       filters,
+                                       ITEM_VARIABLE);
 
         try {
             this.create_proxies ();
@@ -69,37 +92,22 @@ public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
 
     private async void get_children_count () {
         try {
-            // We are performing actual search (though an optimized one) to get
-            // the hitcount rather than GetHitCount because GetHitCount only
-            // allows us to get hit count for Text searches.
-            string query;
-
-            if (this.query_condition != "") {
-                query = "<rdfq:Condition>\n" +
-                            this.query_condition +
-                        "</rdfq:Condition>";
-            } else {
-                query = "";
-            }
-
-            var search_result = yield this.search_proxy.query (
-                                        0,
-                                        this.service,
-                                        new string[0],
-                                        "",
-                                        this.keywords,
-                                        query,
-                                        false,
-                                        new string[0],
-                                        false,
-                                        0,
-                                        -1);
-
-            this.child_count = search_result.length[0];
+            var query = new TrackerQuery.from_template (this.query);
+
+            query.variables = new ArrayList<string> ();
+            query.variables.add ("COUNT(" + ITEM_VARIABLE + ") AS x");
+            query.optional = new ArrayList<TrackerQueryTriplet> ();
+
+            var query_str = query.to_string ();
+
+            debug ("Executing SPARQL query: %s", query_str);
+            var result = yield this.resources.sparql_query (query_str);
+
+            this.child_count = result[0,0].to_int ();
             this.updated ();
         } catch (GLib.Error error) {
-            critical ("error getting items under service '%s': %s",
-                      this.service,
+            critical ("error getting item count under category '%s': %s",
+                      this.category,
                       error.message);
 
             return;
@@ -132,9 +140,10 @@ public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
                                         out uint         total_matches,
                                         Cancellable?     cancellable)
                                         throws GLib.Error {
-        string query = this.create_query_from_expression (expression);
         var results = new ArrayList<MediaObject> ();
-
+        var query = this.create_query (expression,
+                                       (int) offset,
+                                       (int) max_count);
         if (query == null) {
             /* FIXME: chain-up when bug#601558 is fixed
             return yield base.search (expression,
@@ -145,28 +154,17 @@ public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
             return results;
         }
 
-        string[] keys = TrackerItem.get_metadata_keys ();
-
-        var search_result = yield this.search_proxy.query (
-                                        0,
-                                        this.service,
-                                        keys,
-                                        "",
-                                        this.keywords,
-                                        query,
-                                        false,
-                                        new string[0],
-                                        false,
-                                        (int) offset,
-                                        (int) max_count);
+        var query_str = query.to_string ();
+
+        debug ("Executing SPARQL query: %s", query_str);
+        var search_result = yield this.resources.sparql_query (query_str);
 
         /* Iterate through all items */
         for (uint i = 0; i < search_result.length[0]; i++) {
-            string path = search_result[i, 0];
-            string service = search_result[i, 1];
-            string[] metadata = this.slice_strvv_tail (search_result, i, 2);
+            string uri = search_result[i, 0];
+            string[] metadata = this.slice_strvv_tail (search_result, i, 1);
 
-            var item = this.create_item (service, path, metadata);
+            var item = this.create_item (uri, metadata);
             results.add (item);
         }
 
@@ -175,110 +173,81 @@ public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
         return results;
     }
 
-    private string? create_query_from_expression (SearchExpression expression) {
-        string query = null;
-
+    private TrackerQuery? create_query (SearchExpression expression,
+                                        int              offset,
+                                        int              max_count) {
         if (expression == null || !(expression is RelationalExpression)) {
-            return query;
+            return null;
         }
 
         var rel_expression = expression as RelationalExpression;
-        var query_op = this.get_op_for_expression (rel_expression);
+        string filter = null;
 
-        if (rel_expression.operand1 == "@id" && query_op != null) {
-            query = create_query_for_id (rel_expression, query_op);
+        if (rel_expression.operand1 == "@id") {
+            filter = create_filter_for_id (rel_expression);
         } else if (rel_expression.operand1 == "@parentID" &&
-                   rel_expression.compare_string (this.id)) {
-            if (this.query_condition != "") {
-                query = "<rdfq:Condition>\n" +
-                            this.query_condition +
-                        "</rdfq:Condition>";
-            } else {
-                query = "";
-            }
+                   !rel_expression.compare_string (this.id)) {
+            return null;
         }
 
-        return query;
-    }
+        var query = new TrackerQuery.from_template (this.query);
 
-    private string? create_query_for_id (RelationalExpression expression,
-                                         string               query_op) {
-        string query = null;
-        string parent_id;
-        string service;
-
-        var path = this.get_item_info (expression.operand2,
-                                       out parent_id,
-                                       out service);
-
-        if (path != null && parent_id != null && parent_id == this.id) {
-            var dir = Path.get_dirname (path);
-            var basename = Path.get_basename (path);
-
-            var search_condition = "<rdfq:and>\n" +
-                                        "<" + query_op + ">\n" +
-                                            "<rdfq:Property " +
-                                                "name=\"File:Path\" />\n" +
-                                            "<rdf:String>" +
-                                                dir +
-                                            "</rdf:String>\n" +
-                                        "</" + query_op + ">\n" +
-                                        "<" + query_op + ">\n" +
-                                            "<rdfq:Property " +
-                                                "name=\"File:Name\" />\n" +
-                                            "<rdf:String>" +
-                                                basename +
-                                            "</rdf:String>\n" +
-                                        "</" + query_op + ">\n" +
-                                   "</rdfq:and>\n";
-
-            if (this.query_condition != "") {
-                query = "<rdfq:Condition>\n" +
-                            "<rdfq:and>\n" +
-                                search_condition +
-                                this.query_condition +
-                            "</rdfq:and>\n" +
-                        "</rdfq:Condition>";
-            } else {
-                query = "<rdfq:Condition>\n" +
-                            search_condition +
-                        "</rdfq:Condition>";
-            }
+        if (filter != null) {
+            var filters = query.filters;
+            query.filters = new ArrayList<string> ();
+            query.filters.add_all (filters);
+            query.filters.add (filter);
         }
 
+        query.offset = offset;
+        query.max_count = max_count;
+
         return query;
     }
 
-    private string? get_op_for_expression (RelationalExpression expression) {
+    private string? create_filter_for_id (RelationalExpression expression) {
+        string filter = null;
+
         switch (expression.op) {
-        case SearchCriteriaOp.EQ:
-            return "rdfq:equals";
-        case SearchCriteriaOp.CONTAINS:
-            return "rdfq:contains";
-        default:
-            return null;
+            case SearchCriteriaOp.EQ:
+                string parent_id;
+
+                var uri = this.get_item_info (expression.operand2,
+                                              out parent_id);
+                if (uri == null || parent_id == null || parent_id != this.id) {
+                    break;
+                }
+
+                filter = ITEM_VARIABLE + " = " + uri;
+                break;
+            case SearchCriteriaOp.CONTAINS:
+                filter = "regex(" + ITEM_VARIABLE + ", " +
+                                    expression.operand2 +
+                         ")";
+                break;
         }
+
+        return filter;
     }
 
-    private MediaItem? create_item (string   service,
-                                    string   path,
+    private MediaItem? create_item (string   uri,
                                     string[] metadata)
                                     throws GLib.Error {
-        var id = service + ":" + this.id + ":" + path;
+        var id = this.id + ":" + uri;
 
-        if (service == TrackerVideoItem.SERVICE) {
+        if (this.category == TrackerVideoItem.CATEGORY) {
             return new TrackerVideoItem (id,
-                                         path,
+                                         uri,
                                          this,
                                          metadata);
-        } else if (service == TrackerImageItem.SERVICE) {
+        } else if (this.category == TrackerImageItem.CATEGORY) {
             return new TrackerImageItem (id,
-                                         path,
+                                         uri,
                                          this,
                                          metadata);
-        } else if (service == TrackerMusicItem.SERVICE) {
+        } else if (this.category == TrackerMusicItem.CATEGORY) {
             return new TrackerMusicItem (id,
-                                         path,
+                                         uri,
                                          this,
                                          metadata);
         } else {
@@ -286,18 +255,16 @@ public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
         }
     }
 
-    // Returns the path, ID of the parent and service this item belongs to, or
-    // null item_id is invalid
+    // Returns the URI and the ID of the parent this item belongs to, or null
+    // if item_id is invalid
     private string? get_item_info (string     item_id,
-                                   out string parent_id,
-                                   out string service) {
-        var tokens = item_id.split (":", 3);
+                                   out string parent_id) {
+        var tokens = item_id.split (":", 2);
 
-        if (tokens[0] != null && tokens[1] != null && tokens[2] != null) {
-            service = tokens[0];
-            parent_id = tokens[1];
+        if (tokens[0] != null && tokens[1] != null) {
+            parent_id = tokens[0];
 
-            return tokens[2];
+            return tokens[1];
         } else {
             return null;
         }
@@ -306,9 +273,9 @@ public class Rygel.TrackerSearchContainer : Rygel.MediaContainer {
     private void create_proxies () throws DBus.Error {
         DBus.Connection connection = DBus.Bus.get (DBus.BusType.SESSION);
 
-        this.search_proxy = connection.get_object (TRACKER_SERVICE,
-                                                   SEARCH_PATH)
-                                                   as TrackerSearchIface;
+        this.resources = connection.get_object (TRACKER_SERVICE,
+                                                RESOURCES_PATH)
+                                                as TrackerResourcesIface;
     }
 
     /**
diff --git a/src/plugins/tracker/rygel-tracker-video-item.vala b/src/plugins/tracker/rygel-tracker-video-item.vala
index d87ec08..8c777bd 100644
--- a/src/plugins/tracker/rygel-tracker-video-item.vala
+++ b/src/plugins/tracker/rygel-tracker-video-item.vala
@@ -28,7 +28,7 @@ using DBus;
  * Represents Tracker video item.
  */
 public class Rygel.TrackerVideoItem : Rygel.TrackerItem {
-    public const string SERVICE = "Videos";
+    public const string CATEGORY = "nmm:Video";
 
     public TrackerVideoItem (string                 id,
                              string                 path,
@@ -37,25 +37,20 @@ public class Rygel.TrackerVideoItem : Rygel.TrackerItem {
                              throws GLib.Error {
         base (id, path, parent, MediaItem.VIDEO_CLASS, metadata);
 
-        if (metadata[Metadata.VIDEO_TITLE] != "")
-            this.title = metadata[Metadata.VIDEO_TITLE];
+        if (metadata[Metadata.TITLE] != "")
+            this.title = metadata[Metadata.TITLE];
         else
             /* If title wasn't provided, use filename instead */
             this.title = metadata[Metadata.FILE_NAME];
 
-        if (metadata[Metadata.VIDEO_WIDTH] != "")
-            this.width = metadata[Metadata.VIDEO_WIDTH].to_int ();
+        if (metadata[Metadata.WIDTH] != "")
+            this.width = metadata[Metadata.WIDTH].to_int ();
 
-        if (metadata[Metadata.VIDEO_HEIGHT] != "")
-            this.height = metadata[Metadata.VIDEO_HEIGHT].to_int ();
+        if (metadata[Metadata.HEIGHT] != "")
+            this.height = metadata[Metadata.HEIGHT].to_int ();
 
-        if (metadata[Metadata.VIDEO_DURATION] != "")
-            this.duration = metadata[Metadata.VIDEO_DURATION].to_int ();
-
-        if (metadata[Metadata.VIDEO_DURATION] != "")
-            this.duration = metadata[Metadata.VIDEO_DURATION].to_int ();
-
-        this.author = metadata[Metadata.AUTHOR];
+        if (metadata[Metadata.DURATION] != "")
+            this.duration = metadata[Metadata.DURATION].to_int ();
     }
 }
 



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