[rygel/wip/tracker-3.0: 12/17] tracker3: Add plugin to support tracker3



commit bf74f122a923e96b30b772b9a88bb1279a657f53
Author: Jens Georg <mail jensge org>
Date:   Thu May 7 00:49:22 2020 +0200

    tracker3: Add plugin to support tracker3
    
    Mainly just copy the tracker plugin and change the sparql connection.

 meson_options.txt                                  |   2 +-
 src/plugins/tracker3/meson.build                   |  49 +++
 src/plugins/tracker3/rygel-tracker-albums.vala     |  36 ++
 src/plugins/tracker3/rygel-tracker-artists.vala    |  36 ++
 .../rygel-tracker-category-all-container.vala      | 153 +++++++++
 .../tracker3/rygel-tracker-category-container.vala |  85 +++++
 .../tracker3/rygel-tracker-cleanup-query.vala      |  66 ++++
 .../tracker3/rygel-tracker-deletion-query.vala     |  59 ++++
 src/plugins/tracker3/rygel-tracker-genre.vala      |  36 ++
 .../tracker3/rygel-tracker-insertion-query.vala    | 182 ++++++++++
 src/plugins/tracker3/rygel-tracker-interfaces.vala |  51 +++
 .../tracker3/rygel-tracker-item-factory.vala       | 145 ++++++++
 .../tracker3/rygel-tracker-metadata-container.vala | 152 +++++++++
 .../rygel-tracker-metadata-multivalues.vala        | 104 ++++++
 .../tracker3/rygel-tracker-metadata-values.vala    |  90 +++++
 .../tracker3/rygel-tracker-music-item-factory.vala | 143 ++++++++
 src/plugins/tracker3/rygel-tracker-music.vala      |  40 +++
 src/plugins/tracker3/rygel-tracker-new.vala        |  58 ++++
 .../rygel-tracker-picture-item-factory.vala        |  91 +++++
 src/plugins/tracker3/rygel-tracker-pictures.vala   |  37 ++
 .../tracker3/rygel-tracker-plugin-factory.vala     |  59 ++++
 src/plugins/tracker3/rygel-tracker-plugin.vala     |  38 +++
 .../tracker3/rygel-tracker-query-triplet.vala      | 106 ++++++
 .../tracker3/rygel-tracker-query-triplets.vala     |  89 +++++
 src/plugins/tracker3/rygel-tracker-query.vala      | 115 +++++++
 .../tracker3/rygel-tracker-root-container.vala     |  60 ++++
 .../tracker3/rygel-tracker-search-container.vala   | 374 +++++++++++++++++++++
 .../tracker3/rygel-tracker-selection-query.vala    | 221 ++++++++++++
 src/plugins/tracker3/rygel-tracker-tags.vala       |  39 +++
 src/plugins/tracker3/rygel-tracker-titles.vala     |  81 +++++
 .../tracker3/rygel-tracker-upnp-property-map.vala  | 132 ++++++++
 .../tracker3/rygel-tracker-video-item-factory.vala |  96 ++++++
 src/plugins/tracker3/rygel-tracker-videos.vala     |  36 ++
 src/plugins/tracker3/rygel-tracker-years.vala      |  52 +++
 src/plugins/tracker3/tracker3.plugin.in            |   8 +
 35 files changed, 3120 insertions(+), 1 deletion(-)
---
diff --git a/meson_options.txt b/meson_options.txt
index bd61fb60..cb604c4e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,7 +1,7 @@
 option('uninstalled', type: 'boolean', value: 'false', description: 'Run Rygel from build directory only')
 option('api-docs', type: 'boolean', value: 'false', description: 'Build the API documentation')
 option('systemd-user-units-dir', type : 'string', value : 'auto', description : 'Where to install the 
systemd user unit (use special values "auto" or "none", or pass a path')
-option('plugins', type : 'array', choices : ['external', 'gst-launch', 'lms', 'media-export', 'mpris', 
'playbin', 'ruih', 'tracker'])
+option('plugins', type : 'array', choices : ['external', 'gst-launch', 'lms', 'media-export', 'mpris', 
'playbin', 'ruih', 'tracker', 'tracker3'])
 option('engines', type : 'array', choices : ['simple', 'gstreamer'])
 option('examples', type : 'boolean', value : 'true')
 option('tests', type : 'boolean', value : 'true')
diff --git a/src/plugins/tracker3/meson.build b/src/plugins/tracker3/meson.build
new file mode 100644
index 00000000..b43359a6
--- /dev/null
+++ b/src/plugins/tracker3/meson.build
@@ -0,0 +1,49 @@
+configure_file(input : 'tracker3.plugin.in',
+               output : 'tracker3.plugin',
+               configuration : plugin_config,
+               install_dir : rygel_plugindir)
+
+rygel_tracker_sources = [
+    'rygel-tracker-root-container.vala',
+    'rygel-tracker-category-container.vala',
+    'rygel-tracker-music.vala',
+    'rygel-tracker-videos.vala',
+    'rygel-tracker-pictures.vala',
+    'rygel-tracker-metadata-values.vala',
+    'rygel-tracker-metadata-container.vala',
+    'rygel-tracker-metadata-multivalues.vala',
+    'rygel-tracker-tags.vala',
+    'rygel-tracker-years.vala',
+    'rygel-tracker-titles.vala',
+    'rygel-tracker-albums.vala',
+    'rygel-tracker-artists.vala',
+    'rygel-tracker-genre.vala',
+    'rygel-tracker-new.vala',
+    'rygel-tracker-search-container.vala',
+    'rygel-tracker-category-all-container.vala',
+    'rygel-tracker-upnp-property-map.vala',
+    'rygel-tracker-query.vala',
+    'rygel-tracker-selection-query.vala',
+    'rygel-tracker-deletion-query.vala',
+    'rygel-tracker-insertion-query.vala',
+    'rygel-tracker-cleanup-query.vala',
+    'rygel-tracker-query-triplet.vala',
+    'rygel-tracker-query-triplets.vala',
+    'rygel-tracker-item-factory.vala',
+    'rygel-tracker-video-item-factory.vala',
+    'rygel-tracker-music-item-factory.vala',
+    'rygel-tracker-picture-item-factory.vala',
+    'rygel-tracker-plugin.vala',
+    'rygel-tracker-plugin-factory.vala',
+    'rygel-tracker-interfaces.vala']
+
+shared_module('rygel-tracker3',
+              rygel_tracker_sources,
+              dependencies : [
+                  common_deps,
+                  dependency('tracker-sparql-3.0'),
+                  build_config, rygel_core, rygel_server
+              ],
+              c_args : ['-DG_LOG_DOMAIN="RygelTracker3"'],
+              install : true,
+              install_dir : rygel_plugindir)
diff --git a/src/plugins/tracker3/rygel-tracker-albums.vala b/src/plugins/tracker3/rygel-tracker-albums.vala
new file mode 100644
index 00000000..2bc63d33
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-albums.vala
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009,2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * Container listing Music albums as containers.
+ */
+public class Rygel.Tracker.Albums : MetadataValues {
+    public Albums (Music parent) {
+        base (parent.id + "Albums",
+              parent,
+              _("Albums"),
+              parent.item_factory,
+              "upnp:album",
+              MediaContainer.MUSIC_ALBUM);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-artists.vala b/src/plugins/tracker3/rygel-tracker-artists.vala
new file mode 100644
index 00000000..38e37eb6
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-artists.vala
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009,2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * Container listing artists as containers.
+ */
+public class Rygel.Tracker.Artists : MetadataValues {
+    public Artists (Music parent) {
+        base (parent.id + "Artists",
+              parent,
+              _("Artists"),
+              parent.item_factory,
+              "upnp:artist",
+              MediaContainer.MUSIC_ARTIST);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-category-all-container.vala 
b/src/plugins/tracker3/rygel-tracker-category-all-container.vala
new file mode 100644
index 00000000..64ab3aa7
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-category-all-container.vala
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia com>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * A search container that contains all the items in a category.
+ */
+public class Rygel.Tracker.CategoryAllContainer : SearchContainer,
+                                                  WritableContainer,
+                                                  SearchableContainer {
+    /* class-wide constants */
+    private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
+    private const string RESOURCES_PATH = "/org/freedesktop/Tracker1/Resources";
+
+    public ArrayList<string> create_classes { get; set; }
+    public ArrayList<string> search_classes { get; set; }
+
+    private Sparql.Connection resources;
+
+    public CategoryAllContainer (CategoryContainer parent) {
+        base ("All" + parent.id, parent, "All", parent.item_factory);
+
+        this.create_classes = new ArrayList<string> ();
+        this.create_classes.add (item_factory.upnp_class);
+        this.search_classes = new ArrayList<string> ();
+
+        try {
+            this.resources = Sparql.Connection.bus_new ("org.freedesktop.Tracker3.Miner.Files", null);
+        } catch (Error io_error) {
+            critical (_("Failed to create a Tracker connection: %s"),
+                      io_error.message);
+        }
+
+        if (item_factory.upload_dir != null) {
+            try {
+                var uri = Filename.to_uri (item_factory.upload_dir, null);
+                this.add_uri (uri);
+            } catch (ConvertError error) {
+                warning (_("Failed to construct URI for folder “%s”: %s"),
+                        item_factory.upload_dir,
+                        error.message);
+            }
+        }
+
+        try {
+            var connection = Bus.get_sync (BusType.SESSION);
+            connection.signal_subscribe (TRACKER_SERVICE,
+                                         TRACKER_SERVICE + ".Resources",
+                                         "GraphUpdated",
+                                         RESOURCES_PATH,
+                                         this.item_factory.category_iri,
+                                         DBusSignalFlags.NONE,
+                                         this.on_graph_updated);
+        } catch (Error error) {
+            critical (_("Could not subscribe to Tracker signals: %s"),
+                      error.message);
+        }
+
+        var cleanup_query = new CleanupQuery (this.item_factory.category);
+        cleanup_query.execute.begin (this.resources);
+    }
+
+    public async void add_item (MediaFileItem item, Cancellable? cancellable)
+                                throws Error {
+        var urn = yield this.create_entry_in_store (item);
+
+        item.id = this.create_child_id_for_urn (urn);
+        item.parent = this;
+    }
+
+    public async void add_container (MediaContainer container,
+                                     Cancellable? cancellable) throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED (_("Not supported"));
+    }
+
+    public async void remove_item (string id, Cancellable? cancellable)
+                                   throws Error {
+        string parent_id;
+
+        var urn = this.get_item_info (id, out parent_id);
+
+        yield this.remove_entry_from_store (urn);
+    }
+
+    public async void remove_container (string id, Cancellable? cancellable)
+                                        throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED ("Not supported");
+    }
+
+    public async MediaObjects? search (SearchExpression? expression,
+                                       uint              offset,
+                                       uint              max_count,
+                                       string            sort_criteria,
+                                       Cancellable?      cancellable,
+                                       out uint          total_matches)
+                                       throws Error {
+        return yield this.simple_search (expression,
+                                         offset,
+                                         max_count,
+                                         sort_criteria,
+                                         cancellable,
+                                         out total_matches);
+    }
+
+    private void on_graph_updated (DBusConnection connection,
+                                   string?        sender,
+                                   string         object_path,
+                                   string         interface_name,
+                                   string         signal_path,
+                                   Variant        parameters) {
+        this.get_children_count.begin ();
+    }
+
+    private async string create_entry_in_store (MediaFileItem item)
+                                                throws Error {
+        var category = this.item_factory.category;
+        var query = new InsertionQuery (item, category);
+
+        yield query.execute (this.resources);
+
+        return query.id;
+    }
+
+    private async void remove_entry_from_store (string id) throws Error {
+        var query = new DeletionQuery (id);
+
+        yield query.execute (this.resources);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-category-container.vala 
b/src/plugins/tracker3/rygel-tracker-category-container.vala
new file mode 100644
index 00000000..351df188
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-category-container.vala
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Container listing content hierarchy for a specific category.
+ */
+public abstract class Rygel.Tracker.CategoryContainer : Rygel.SimpleContainer {
+    public ItemFactory item_factory;
+
+    private CategoryAllContainer all_container;
+
+    protected CategoryContainer (string         id,
+                                 MediaContainer parent,
+                                 string         title,
+                                 ItemFactory    item_factory) {
+        base (id, parent, title);
+
+        this.item_factory = item_factory;
+
+        this.all_container = new CategoryAllContainer (this);
+
+        this.add_child_container (this.all_container);
+        this.add_child_container (new Tags (this, item_factory));
+        this.add_child_container (new Titles (this, this.item_factory));
+        this.add_child_container (new New (this, this.item_factory));
+        ulong signal_id = 0;
+
+        signal_id = this.all_container.container_updated.connect( () => {
+            // ingore first update
+            this.all_container.container_updated.connect
+                                        (this.on_all_container_updated);
+            this.all_container.disconnect (signal_id);
+        });
+    }
+
+    public void add_create_class (string create_class) {
+        this.all_container.create_classes.add (create_class);
+    }
+
+    private void trigger_child_update (MediaObjects children) {
+        foreach (var container in children) {
+            if (container == this.all_container ||
+                container == null) {
+                continue;
+            }
+
+            if (container is MetadataValues) {
+                ((MetadataValues) container).fetch_metadata_values.begin ();
+            } else if (container is SearchContainer) {
+                ((SearchContainer) container).get_children_count.begin ();
+            }
+        }
+    }
+
+    private void on_all_container_updated (MediaContainer other) {
+        if (other != this.all_container) {
+            // otherwise we'd do a recursive update
+            return;
+        }
+
+        this.trigger_child_update (this.get_all_children ());
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-cleanup-query.vala 
b/src/plugins/tracker3/rygel-tracker-cleanup-query.vala
new file mode 100644
index 00000000..73c92ac7
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-cleanup-query.vala
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011-2012 Nokia Corporation.
+ *
+ * Author: Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Represents Tracker SPARQL Deletion query
+ */
+public class Rygel.Tracker.CleanupQuery : Query {
+    private string category;
+
+    public CleanupQuery (string category) {
+        var triplets = new QueryTriplets ();
+        triplets.add (new QueryTriplet ("?r", "a", "rdfs:Resource"));
+
+        base (triplets);
+
+        this.category = category;
+    }
+
+    public override async void execute (Sparql.Connection resources)
+                                        throws Error,
+                                               IOError,
+                                               Sparql.Error,
+                                               DBusError {
+        var str = this.to_string ();
+
+        debug ("Executing SPARQL query: %s", str);
+
+        yield resources.update_async (str);
+    }
+
+    public override string to_string () {
+        StringBuilder result = new StringBuilder ();
+
+        result.append ("DELETE {");
+        result.append (base.to_string ());
+        result.append ("} WHERE {");
+        result.append_printf ("?r a nie:DataObject, %s . ", this.category);
+        result.append (" ?r nie:generator \"rygel\". ");
+        result.append ("FILTER(NOT EXISTS { ?r ");
+        result.append ("a nfo:FileDataObject. })}");
+
+        return result.str;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-deletion-query.vala 
b/src/plugins/tracker3/rygel-tracker-deletion-query.vala
new file mode 100644
index 00000000..537acc4d
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-deletion-query.vala
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali <zeenix gmail com>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Represents Tracker SPARQL Deletion query
+ */
+public class Rygel.Tracker.DeletionQuery : Query {
+    private string id;
+
+    public DeletionQuery (string id) {
+        var triplets = new QueryTriplets ();
+        triplets.add (new QueryTriplet ("<" + id + ">", "a", "rdfs:Resource"));
+
+        base (triplets);
+
+        this.id = id;
+    }
+
+    public override async void execute (Sparql.Connection resources)
+                                        throws Error,
+                                               IOError,
+                                               Sparql.Error,
+                                               DBusError {
+        var str = this.to_string ();
+
+        debug ("Executing SPARQL query: %s", str);
+
+        yield resources.update_async (str);
+
+        debug ("Deleted item '%s' from Tracker store", this.id);
+    }
+
+    public override string to_string () {
+        return "DELETE { " + base.to_string () + " }";
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-genre.vala b/src/plugins/tracker3/rygel-tracker-genre.vala
new file mode 100644
index 00000000..47c90cc0
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-genre.vala
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009,2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * Container listing music genres as containers.
+ */
+public class Rygel.Tracker.Genre : MetadataValues {
+    public Genre (Music parent) {
+        base (parent.id + "Genre",
+              parent,
+              _("Genre"),
+              parent.item_factory,
+              "upnp:genre",
+              MediaContainer.MUSIC_GENRE);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-insertion-query.vala 
b/src/plugins/tracker3/rygel-tracker-insertion-query.vala
new file mode 100644
index 00000000..e062d058
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-insertion-query.vala
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali <zeenix gmail com>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Represents Tracker SPARQL Insertion query
+ */
+public class Rygel.Tracker.InsertionQuery : Query {
+    private const string TEMP_ID = "x";
+    private const string QUERY_ID = "_:" + TEMP_ID;
+
+    private const string MINER_SERVICE = "org.freedesktop.Tracker1.Miner.Files.Index";
+    private const string MINER_PATH = "/org/freedesktop/Tracker1/Miner/Files/Index";
+
+    // We need to add the size in the miner's graph so that the miner will
+    // update it and correct a (possibly wrong) size we got via CreateItem
+    // (DLNA requirement 7.3.128.7)
+    // FIXME: Use constant from libtracker-miner once we port to
+    // libtracker-sparql
+    private const string MINER_GRAPH =
+                              "urn:uuid:472ed0cc-40ff-4e37-9c0c-062d78656540";
+
+    private const string RESOURCE_ID_QUERY_TEMPLATE =
+        "SELECT ?resource WHERE { ?resource a nie:DataObject; nie:url '%s' }";
+
+    private const string RESOURCE_NOT_BOUND_TEMPLATE =
+        "OPTIONAL { ?resource a nie:DataObject; nie:url '%s' } " +
+        "FILTER (!bound(?resource))";
+
+    public string id;
+
+    private string uri;
+
+    public InsertionQuery (MediaFileItem item, string category) {
+        var type = "nie:DataObject";
+        var file = File.new_for_uri (item.get_primary_uri ());
+
+        if (!file.is_native ()) {
+            type = "nfo:RemoteDataObject";
+        }
+
+        var triplets = new QueryTriplets ();
+        triplets.add (new QueryTriplet (QUERY_ID, "a", category));
+        triplets.add (new QueryTriplet (QUERY_ID, "a", type));
+        triplets.add (new QueryTriplet (QUERY_ID, "nmm:uPnPShared", "true"));
+        triplets.add (new QueryTriplet (QUERY_ID, "tracker:available", "true"));
+        triplets.add (new QueryTriplet (QUERY_ID,
+                                        "nie:generator",
+                                        "\"rygel\""));
+
+        triplets.add (new QueryTriplet (QUERY_ID,
+                                        "nie:title",
+                                        "\"" + item.title + "\""));
+
+        triplets.add (new QueryTriplet.with_graph
+                                        (MINER_GRAPH,
+                                         QUERY_ID,
+                                         "nie:mimeType",
+                                         "\"" + item.mime_type + "\""));
+        var dlna_profile = "";
+        if (item.dlna_profile != null) {
+            dlna_profile = item.dlna_profile;
+        }
+
+        triplets.add (new QueryTriplet.with_graph
+                                        (MINER_GRAPH,
+                                         QUERY_ID,
+                                         "nmm:dlnaProfile",
+                                         "\"" + dlna_profile + "\""));
+
+        triplets.add (new QueryTriplet (QUERY_ID,
+                                        "nie:url",
+                                        "\"" + item.get_primary_uri () + "\""));
+        string date;
+        if (item.date == null) {
+            var now = new GLib.DateTime.now_utc ();
+            date = "%sZ".printf (now.format ("%Y-%m-%dT%H:%M:%S"));
+        } else {
+            // Rygel core makes sure that this is a valid ISO8601 date.
+            date = item.date;
+        }
+        triplets.add (new QueryTriplet (QUERY_ID,
+                                        "nie:contentCreated",
+                                        "\"" + date + "\""));
+
+        if (item.size > 0) {
+            triplets.add (new QueryTriplet.with_graph
+                                        (MINER_GRAPH,
+                                         QUERY_ID,
+                                         "nie:byteSize",
+                                         "\"" + item.size.to_string () + "\""));
+        }
+
+        base (triplets);
+
+        this.uri = item.get_primary_uri ();
+    }
+
+    public override async void execute (Sparql.Connection resources)
+                                        throws Error,
+                                        IOError,
+                                        Sparql.Error,
+                                        DBusError {
+        var str = this.to_string ();
+
+        debug ("Executing SPARQL query: %s", str);
+
+        Variant v = yield resources.update_blank_async (str);
+        VariantIter iter1, iter2, iter3;
+        string key = null;
+
+        iter1 = v.iterator ();
+        while (iter1.next ("aa{ss}", out iter2)) {
+            while (iter2.next ("a{ss}", out iter3)) {
+                while (iter3.next ("{ss}", out key, out this.id)) {
+                    break;
+                }
+            }
+        }
+
+        // Item already existed
+        if (this.id == null)  {
+            var cursor = yield resources.query_async
+                                        (this.get_resource_id_query ());
+
+            try {
+                while (cursor.next ()) {
+                    this.id = cursor.get_string (0);
+                    break;
+                }
+            } catch (Error error) {
+            }
+        } else {
+            var file = File.new_for_uri (this.uri);
+            if (file.is_native () &&
+                file.query_exists ()) {
+                MinerFilesIndexIface miner  = Bus.get_proxy_sync
+                                        (BusType.SESSION,
+                                         MINER_SERVICE,
+                                         MINER_PATH,
+                                         DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+                miner.index_file.begin (this.uri);
+            }
+        }
+
+        debug ("Created item in Tracker store with ID '%s'", this.id);
+    }
+
+    public override string to_string () {
+        var query = "INSERT { " + base.to_string () + " }";
+        query += "WHERE {" + RESOURCE_NOT_BOUND_TEMPLATE.printf (this.uri) +
+                 "}";
+
+        return query;
+    }
+
+    private string get_resource_id_query () {
+        return RESOURCE_ID_QUERY_TEMPLATE.printf (this.uri);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-interfaces.vala 
b/src/plugins/tracker3/rygel-tracker-interfaces.vala
new file mode 100644
index 00000000..e4b21a0b
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-interfaces.vala
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+public struct Event {
+    int graph_id;
+    int subject_id;
+    int pred_id;
+    int object_id;
+}
+
+[DBus (name = "org.freedesktop.Tracker1.Statistics")]
+public interface Rygel.Tracker.StatsIface : DBusProxy {
+    public abstract string[,] get () throws IOError, DBusError;
+}
+
+[DBus (name = "org.freedesktop.Tracker1.Resources")]
+public interface Rygel.Tracker.ResourcesIface: DBusProxy {
+    public abstract async string[,] sparql_query (string query)
+                                                  throws IOError, DBusError;
+    public abstract async void sparql_update (string query)
+                                              throws IOError, DBusError;
+    public abstract async HashTable<string,string>[,] sparql_update_blank
+                                        (string query)
+                                         throws IOError, DBusError;
+}
+
+[DBus (name = "org.freedesktop.Tracker1.Miner.Files.Index")]
+public interface Rygel.Tracker.MinerFilesIndexIface: DBusProxy {
+    public abstract async void index_file (string uri)
+                                           throws IOError, DBusError;
+}
diff --git a/src/plugins/tracker3/rygel-tracker-item-factory.vala 
b/src/plugins/tracker3/rygel-tracker-item-factory.vala
new file mode 100644
index 00000000..5c069f80
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-item-factory.vala
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ * Copyright (C) 2010 MediaNet Inh.
+ * Copyright (C) 2012 Intel Corporation.
+ * Copyright (C) 2013 Cable Television Laboratories, Inc.
+ *
+ * Authors: Zeeshan Ali <zeenix gmail com>
+ *          Sunil Mohan Adapa <sunil medhas org>
+ *          Jens Georg <jensg openismus com>
+ *          Doug Galligan <doug sentosatech 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using GUPnP;
+using Tracker;
+
+/**
+ * Abstract Tracker item factory.
+ */
+public abstract class Rygel.Tracker.ItemFactory {
+    protected enum Metadata {
+        TRACKER_ID,
+        URL,
+        PLACE_HOLDER,
+        FILE_NAME,
+        TITLE,
+        DLNA_PROFILE,
+        MIME,
+        SIZE,
+        DATE,
+
+        LAST_KEY
+    }
+
+    public string category;
+    public string category_iri;
+    public string upnp_class;
+    public string? upload_dir;
+
+    public ArrayList<string> properties;
+
+    protected ItemFactory (string  category,
+                           string  category_iri,
+                           string  upnp_class,
+                           string? upload_dir) {
+        this.category = category;
+        this.category_iri = category_iri;
+        this.upnp_class = upnp_class;
+        this.upload_dir = upload_dir;
+
+        message ("Using %s as upload directory for %s",
+                 upload_dir == null ? "none" : upload_dir,
+                 upnp_class);
+
+        this.properties = new ArrayList<string> ();
+
+        // These must be the same order as enum Metadata
+        this.properties.add ("res");
+        this.properties.add ("place_holder");
+        this.properties.add ("fileName");
+        this.properties.add ("dc:title");
+        this.properties.add ("dlnaProfile");
+        this.properties.add ("mimeType");
+        this.properties.add ("res@size");
+        this.properties.add ("date");
+    }
+
+    public abstract MediaFileItem create (string          id,
+                                          string          uri,
+                                          SearchContainer parent,
+                                          Sparql.Cursor   metadata)
+                                          throws GLib.Error;
+
+    protected void set_ref_id (MediaFileItem item, string prefix) {
+        if (item.id.has_prefix (prefix)) {
+            return;
+        }
+
+        var split_id = item.id.split (",");
+        if (split_id.length != 2) {
+            return;
+        }
+
+        item.ref_id = prefix + "," + split_id[1];
+    }
+
+    protected virtual void set_metadata (MediaFileItem item,
+                                         string        uri,
+                                         Sparql.Cursor metadata)
+                                         throws GLib.Error {
+        if (metadata.is_bound (Metadata.TITLE)) {
+            item.title = metadata.get_string (Metadata.TITLE);
+        } else {
+            /* If title wasn't provided, use filename instead */
+            item.title = metadata.get_string (Metadata.FILE_NAME);
+        }
+
+        if (metadata.is_bound (Metadata.SIZE)) {
+            item.size = metadata.get_integer (Metadata.SIZE);
+        } else {
+            // If its in tracker store and size is unknown, it most probably
+            // means the size is 0 (i-e a place-holder empty item that we
+            // created).
+            item.size = 0;
+        }
+
+        item.place_holder = metadata.get_boolean (Metadata.PLACE_HOLDER);
+
+        if (metadata.is_bound (Metadata.DATE)) {
+            item.date = metadata.get_string (Metadata.DATE);
+        }
+
+        if (metadata.is_bound (Metadata.DLNA_PROFILE)) {
+            item.dlna_profile = metadata.get_string (Metadata.DLNA_PROFILE);
+        }
+
+        if (metadata.is_bound (Metadata.MIME)) {
+            item.mime_type = metadata.get_string (Metadata.MIME);
+        }
+
+        item.add_uri (uri);
+    }
+
+    protected virtual void add_resources (MediaFileItem item)
+                                          throws GLib.Error {
+        item.add_engine_resources.begin ();
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-metadata-container.vala 
b/src/plugins/tracker3/rygel-tracker-metadata-container.vala
new file mode 100644
index 00000000..1637fef3
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-metadata-container.vala
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GUPnP;
+using Gee;
+using Tracker;
+
+/**
+ * Base class for containers listing possible values of a particular Tracker
+ * metadata key.
+ */
+public abstract class Rygel.Tracker.MetadataContainer : Rygel.SimpleContainer {
+    /* class-wide constants */
+    private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
+    private const string RESOURCES_PATH = "/org/freedesktop/Tracker1/Resources";
+
+    protected ItemFactory item_factory;
+    private bool update_in_progress = false;
+
+    private string child_class;
+
+    private Sparql.Connection resources;
+    protected QueryTriplets triplets;
+
+    protected MetadataContainer (string         id,
+                                 MediaContainer parent,
+                                 string         title,
+                                 ItemFactory    item_factory,
+                                 string?        child_class = null) {
+        base (id, parent, title);
+
+        this.item_factory = item_factory;
+        this.child_class = child_class;
+
+        try {
+            this.resources = Sparql.Connection.bus_new ("org.freedesktop.Tracker3.Miner.Files", null);
+        } catch (Error error) {
+            critical (_("Failed to create Tracker connection: %s"),
+                      error.message);
+
+            return;
+        }
+    }
+
+    internal async void fetch_metadata_values () {
+        if (this.update_in_progress) {
+            return;
+        }
+
+        this.update_in_progress = true;
+        // First thing, clear the existing hierarchy, if any
+        this.clear ();
+
+        var query = this.create_query ();
+
+        try {
+            yield query.execute (this.resources);
+
+            /* Iterate through all the values */
+            while (query.result.next ()) {
+                if (!query.result.is_bound (0)) {
+                    continue;
+                }
+
+                var value = query.result.get_string (0);
+
+                if (value == "") {
+                    continue;
+                }
+
+                var title = this.create_title_for_value (value);
+                if (title == null) {
+                    continue;
+                }
+
+                var id = this.create_id_for_title (title);
+                if (id == null || !this.is_child_id_unique (id)) {
+                    continue;
+                }
+
+                var container = this.create_container (id, title, value);
+
+                if (this.child_class != null) {
+                    container.upnp_class = child_class;
+                }
+
+                this.add_child_container (container);
+            }
+        } catch (Error error) {
+            critical (_("Error getting all values for “%s”: %s"),
+                      this.id,
+                      error.message);
+            this.update_in_progress = false;
+
+            return;
+        }
+
+        this.updated ();
+        this.update_in_progress = false;
+    }
+
+    protected abstract SelectionQuery create_query ();
+    protected abstract SearchContainer create_container (string id,
+                                                         string title,
+                                                         string value);
+
+    public override async MediaObject? find_object (string       id,
+                                                    Cancellable? cancellable)
+                                                    throws GLib.Error {
+        if (this.is_our_child (id)) {
+            return yield base.find_object (id, cancellable);
+        } else {
+            return null;
+        }
+    }
+
+    protected virtual string? create_id_for_title (string title) {
+        return this.id + ":" + Uri.escape_string (title, "", true);
+    }
+
+    protected virtual string? create_title_for_value (string value) {
+        return value;
+    }
+
+    protected virtual string create_filter (string variable, string value) {
+        return variable + " = \"" + Query.escape_string (value) + "\"";
+    }
+
+    private bool is_our_child (string id) {
+        return id.has_prefix (this.id + ":");
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-metadata-multivalues.vala 
b/src/plugins/tracker3/rygel-tracker-metadata-multivalues.vala
new file mode 100644
index 00000000..a5d94716
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-metadata-multivalues.vala
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali <zeenix gmail com>
+ *         Jens Georg <jensg openismus com>
+ *         Jens Georg <mail jensge org>
+ *
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GUPnP;
+using Gee;
+
+/**
+ * Container listing possible values of a particuler Tracker metadata key.
+ * This class is used for multivalue properties such as nao:Tag (via
+ * nao:hasTag)
+ */
+public abstract class Rygel.Tracker.MetadataMultiValues : MetadataContainer {
+    public string[] key_chain;
+
+    protected MetadataMultiValues (string         id,
+                                   MediaContainer parent,
+                                   string         title,
+                                   ItemFactory    item_factory,
+                                   string[]       key_chain,
+                                   string?        child_class = null) {
+        base (id, parent, title, item_factory, child_class);
+
+        this.key_chain = key_chain;
+
+        this.fetch_metadata_values.begin ();
+    }
+
+    protected override SelectionQuery create_query () {
+        this.triplets = new QueryTriplets ();
+
+        this.triplets.add (new QueryTriplet (SelectionQuery.ITEM_VARIABLE,
+                                             "a",
+                                             this.item_factory.category));
+
+        // All variables used in the query
+        var num_keys = this.key_chain.length - 1;
+        var variables = new string[num_keys];
+        for (int i = 0; i < num_keys; i++) {
+            variables[i] = "?" + key_chain[i].replace (":", "_");
+
+            string subject;
+            if (i == 0) {
+                subject = SelectionQuery.ITEM_VARIABLE;
+            } else {
+                subject = variables[i - 1];
+            }
+
+            this.triplets.add (new QueryTriplet (subject,
+                                                 this.key_chain[i],
+                                                 variables[i]));
+        }
+
+        // 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);
+
+        return new SelectionQuery (selected, triplets, null, last_variable);
+    }
+
+    protected override SearchContainer create_container (string id,
+                                                         string title,
+                                                         string value) {
+
+        // The child container can use the same triplets we used in our
+        // query.
+        var child_triplets = new QueryTriplets.clone (triplets);
+
+        // However we constrain the object of our last triplet.
+        var filters = new ArrayList<string> ();
+        var filter = this.create_filter (child_triplets.last ().obj, value);
+        filters.add (filter);
+
+        return new SearchContainer (id,
+                                    this,
+                                    title,
+                                    this.item_factory,
+                                    child_triplets,
+                                    filters);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-metadata-values.vala 
b/src/plugins/tracker3/rygel-tracker-metadata-values.vala
new file mode 100644
index 00000000..e9fd1606
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-metadata-values.vala
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali <zeenix gmail com>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GUPnP;
+using Gee;
+using Tracker;
+
+/**
+ * Container listing possible values of a particuler Tracker metadata key.
+ * The key needs to be single-valued.
+ */
+public abstract class Rygel.Tracker.MetadataValues : MetadataContainer {
+    private string property;
+
+    protected MetadataValues (string         id,
+                              MediaContainer parent,
+                              string         title,
+                              ItemFactory    item_factory,
+                              string         property,
+                              string?        child_class = null) {
+        base (id, parent, title, item_factory, child_class);
+
+        this.property = property;
+
+        this.triplets = new QueryTriplets ();
+
+        this.triplets.add (new QueryTriplet (SelectionQuery.ITEM_VARIABLE,
+                                             "a",
+                                             this.item_factory.category));
+        this.fetch_metadata_values.begin ();
+    }
+
+    protected override SelectionQuery create_query () {
+        var property_map = UPnPPropertyMap.get_property_map ();
+        var selected = new ArrayList<string> ();
+        selected.add ("DISTINCT " +
+                      property_map[this.property] +
+                      " AS ?x");
+
+        return new SelectionQuery (selected, triplets, null, "?x");
+    }
+
+    protected override SearchContainer create_container (string id,
+                                                         string title,
+                                                         string value) {
+        // The child container can use the same triplets we used in our
+        // query.
+        var child_triplets = new QueryTriplets.clone (this.triplets);
+
+        // However we constrain the object of our last triplet.
+        var filters = new ArrayList<string> ();
+        var property_map = UPnPPropertyMap.get_property_map ();
+        var property = property_map[this.property];
+        var filter = this.create_filter (property, value);
+        filters.add (filter);
+
+        var child = new SearchContainer (id,
+                                         this,
+                                         title,
+                                         this.item_factory,
+                                         child_triplets,
+                                         filters);
+        if (this.property == "upnp:album") {
+            child.sort_criteria = MediaContainer.ALBUM_SORT_CRITERIA;
+        }
+
+        return child;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-music-item-factory.vala 
b/src/plugins/tracker3/rygel-tracker-music-item-factory.vala
new file mode 100644
index 00000000..9a273999
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-music-item-factory.vala
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ * Copyright (C) 2010 MediaNet Inh.
+ *
+ * Authors: Zeeshan Ali <zeenix gmail com>
+ *          Sunil Mohan Adapa <sunil medhas org>
+ *          Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Tracker music item factory.
+ */
+public class Rygel.Tracker.MusicItemFactory : ItemFactory {
+    private enum MusicMetadata {
+        DURATION = Metadata.LAST_KEY,
+        AUDIO_ALBUM,
+        AUDIO_ARTIST,
+        AUDIO_TRACK_NUM,
+        AUDIO_GENRE,
+        SAMPLE_RATE,
+        CHANNELS,
+        BITS_PER_SAMPLE,
+        BITRATE,
+
+        LAST_KEY
+    }
+
+    private const string CATEGORY = "nmm:MusicPiece";
+    private const string CATEGORY_IRI = "http://www.tracker-project.org/"; +
+                                        "temp/nmm#MusicPiece";
+
+    public MusicItemFactory () {
+        var upload_folder = Environment.get_user_special_dir
+                                        (UserDirectory.MUSIC);
+        try {
+            var config = MetaConfig.get_default ();
+            upload_folder = config.get_music_upload_folder ();
+        } catch (Error error) {};
+
+        base (CATEGORY, CATEGORY_IRI, MusicItem.UPNP_CLASS, upload_folder);
+
+        // These must be the same order as enum MusicMetadata
+        this.properties.add ("res@duration");
+        this.properties.add ("upnp:album");
+        this.properties.add ("upnp:artist");
+        this.properties.add ("upnp:originalTrackNumber");
+        this.properties.add ("upnp:genre");
+        this.properties.add ("sampleRate");
+        this.properties.add ("upnp:nrAudioChannels");
+        this.properties.add ("upnp:bitsPerSample");
+        this.properties.add ("upnp:bitrate");
+    }
+
+    public override MediaFileItem create (string          id,
+                                          string          uri,
+                                          SearchContainer parent,
+                                          Sparql.Cursor   metadata)
+                                          throws GLib.Error {
+        var item = new MusicItem (id, parent, "");
+
+        this.set_metadata (item, uri, metadata);
+
+        return item;
+    }
+
+    protected override void set_metadata (MediaFileItem item,
+                                          string        uri,
+                                          Sparql.Cursor metadata)
+                                          throws GLib.Error {
+        base.set_metadata (item, uri, metadata);
+
+        this.set_ref_id (item, "AllMusic");
+
+        var music = item as MusicItem;
+
+        if (metadata.is_bound (MusicMetadata.DURATION) &&
+            metadata.get_string (MusicMetadata.DURATION) != "0") {
+            music.duration = (long) metadata.get_integer
+                                        (MusicMetadata.DURATION);
+        }
+
+        if (metadata.is_bound (MusicMetadata.SAMPLE_RATE)) {
+            music.sample_freq = (int) metadata.get_integer
+                                        (MusicMetadata.SAMPLE_RATE);
+        }
+
+        if (metadata.is_bound (MusicMetadata.CHANNELS)) {
+            music.channels = (int) metadata.get_integer
+                                        (MusicMetadata.CHANNELS);
+        }
+
+        if (metadata.is_bound (MusicMetadata.BITS_PER_SAMPLE)) {
+            music.bits_per_sample = (int) metadata.get_integer
+                                        (MusicMetadata.BITS_PER_SAMPLE);
+        }
+
+        if (metadata.is_bound (MusicMetadata.BITRATE)) {
+            music.bitrate = (int) metadata.get_integer
+                                        (MusicMetadata.BITRATE) / 8;
+        }
+
+        if (metadata.is_bound (MusicMetadata.AUDIO_TRACK_NUM)) {
+            music.track_number = (int) metadata.get_integer
+                                        (MusicMetadata.AUDIO_TRACK_NUM);
+        }
+
+        if (metadata.is_bound (MusicMetadata.AUDIO_ARTIST)) {
+            music.artist = metadata.get_string (MusicMetadata.AUDIO_ARTIST);
+        }
+
+        if (metadata.is_bound (MusicMetadata.AUDIO_ALBUM)) {
+            music.album = metadata.get_string (MusicMetadata.AUDIO_ALBUM);
+        }
+
+        if (metadata.is_bound (MusicMetadata.AUDIO_GENRE)) {
+            music.genre = metadata.get_string (MusicMetadata.AUDIO_GENRE);
+        }
+
+        music.lookup_album_art ();
+
+        base.add_resources(music);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-music.vala b/src/plugins/tracker3/rygel-tracker-music.vala
new file mode 100644
index 00000000..dc81cf00
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-music.vala
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Container listing Music content hierarchy.
+ */
+public class Rygel.Tracker.Music : CategoryContainer {
+    public Music (string id, MediaContainer parent, string title) {
+        base (id, parent, title, new MusicItemFactory ());
+
+        this.add_child_container (new Artists (this));
+        this.add_child_container (new Albums (this));
+        this.add_child_container (new Genre (this));
+        this.search_classes.add (AudioItem.UPNP_CLASS);
+        this.search_classes.add (MusicItem.UPNP_CLASS);
+        this.add_create_class (AudioItem.UPNP_CLASS);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-new.vala b/src/plugins/tracker3/rygel-tracker-new.vala
new file mode 100644
index 00000000..efabcc86
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-new.vala
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
+ * Copyright (C) 2008,2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * A container listing all newly (<3 days) added items.
+ */
+public class Rygel.Tracker.New : Rygel.Tracker.SearchContainer {
+    private const string ADDED_PREDICATE = "tracker:added";
+    private const string ADDED_VARIABLE = "?added";
+    private const long THREE_DAYS_AS_SEC = 259200;
+
+    public New (MediaContainer parent, ItemFactory item_factory) {
+        var triplets = new QueryTriplets ();
+
+        triplets.add (new QueryTriplet (SelectionQuery.ITEM_VARIABLE,
+                                        "a",
+                                        item_factory.category));
+        triplets.add (new QueryTriplet (SelectionQuery.ITEM_VARIABLE,
+                                        ADDED_PREDICATE,
+                                        ADDED_VARIABLE));
+
+        var now = new DateTime.now_utc ();
+        now.add_days (-3);
+        var three_days_ago = "%sZ".printf (now.format ("%Y-%m-%dT%H:%M:%S"));
+
+        var filters = new ArrayList<string> ();
+        filters.add (ADDED_VARIABLE + " > \"" + three_days_ago + "\"");
+
+        base (parent.id + "New",
+              parent,
+              "New",
+              item_factory,
+              triplets,
+              filters);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-picture-item-factory.vala 
b/src/plugins/tracker3/rygel-tracker-picture-item-factory.vala
new file mode 100644
index 00000000..e937fc2d
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-picture-item-factory.vala
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008 Nokia Corporation.
+ * Copyright (C) 2010 MediaNet Inh.
+ *
+ * Authors: Zeeshan Ali <zeenix gmail com>
+ *          Sunil Mohan Adapa <sunil medhas org>
+ *
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Tracker picture item factory.
+ */
+public class Rygel.Tracker.PictureItemFactory : ItemFactory {
+    private enum PictureMetadata {
+        HEIGHT = Metadata.LAST_KEY,
+        WIDTH,
+
+        LAST_KEY
+    }
+
+    private const string CATEGORY = "nmm:Photo";
+    private const string CATEGORY_IRI = "http://www.tracker-project.org/"; +
+                                        "temp/nmm#Photo";
+
+    public PictureItemFactory () {
+        var upload_folder = Environment.get_user_special_dir
+                                        (UserDirectory.PICTURES);
+        try {
+            var config = MetaConfig.get_default ();
+            upload_folder = config.get_picture_upload_folder ();
+        } catch (Error error) {};
+
+        base (CATEGORY, CATEGORY_IRI, PhotoItem.UPNP_CLASS, upload_folder);
+
+        // These must be in the same order as enum PictureMetadata
+        this.properties.add ("height");
+        this.properties.add ("width");
+    }
+
+    public override MediaFileItem create (string          id,
+                                          string          uri,
+                                          SearchContainer parent,
+                                          Sparql.Cursor   metadata)
+                                          throws GLib.Error {
+        var item = new PhotoItem (id, parent, "");
+
+        this.set_metadata (item, uri, metadata);
+
+        return item;
+    }
+
+    protected override void set_metadata (MediaFileItem item,
+                                          string        uri,
+                                          Sparql.Cursor metadata)
+                                          throws GLib.Error {
+        base.set_metadata (item, uri, metadata);
+
+        this.set_ref_id (item, "AllPictures");
+
+        var photo = item as PhotoItem;
+
+        if (metadata.is_bound (PictureMetadata.WIDTH)) {
+            photo.width = (int) metadata.get_integer (PictureMetadata.WIDTH);
+        }
+
+        if (metadata.is_bound (PictureMetadata.HEIGHT)) {
+            photo.height = (int) metadata.get_integer (PictureMetadata.HEIGHT);
+        }
+
+        base.add_resources(photo);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-pictures.vala 
b/src/plugins/tracker3/rygel-tracker-pictures.vala
new file mode 100644
index 00000000..78c2c1f5
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-pictures.vala
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Container listing Pictures content hierarchy.
+ */
+public class Rygel.Tracker.Pictures : CategoryContainer {
+    public Pictures (string id, MediaContainer parent, string title) {
+        base (id, parent, title, new PictureItemFactory ());
+
+        this.add_child_container (new Years (this, this.item_factory));
+        this.search_classes.add (ImageItem.UPNP_CLASS);
+        this.add_create_class (ImageItem.UPNP_CLASS);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-plugin-factory.vala 
b/src/plugins/tracker3/rygel-tracker-plugin-factory.vala
new file mode 100644
index 00000000..e7c74c92
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-plugin-factory.vala
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
+ * Copyright (C) 2009-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia com>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Rygel;
+using Gee;
+
+private Rygel.Tracker.PluginFactory plugin_factory;
+
+public void module_init (PluginLoader loader) {
+    try {
+        plugin_factory = new Rygel.Tracker.PluginFactory (loader);
+    } catch (Error err) {
+        warning (_("Failed to start Tracker service: %s. Plugin disabled."),
+                 err.message);
+    }
+}
+
+public class Rygel.Tracker.PluginFactory {
+    private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
+    private const string STATISTICS_OBJECT =
+                                        "/org/freedesktop/Tracker1/Statistics";
+
+    StatsIface stats;
+    PluginLoader loader;
+
+    public PluginFactory (PluginLoader loader) throws IOError, DBusError {
+        this.stats = Bus.get_proxy_sync (BusType.SESSION,
+                                         TRACKER_SERVICE,
+                                         STATISTICS_OBJECT,
+                                         DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+        this.loader = loader;
+
+        this.stats.get ();
+
+        this.loader.add_plugin (new Tracker.Plugin ());
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-plugin.vala b/src/plugins/tracker3/rygel-tracker-plugin.vala
new file mode 100644
index 00000000..d01cb528
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-plugin.vala
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ *
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+public class Rygel.Tracker.Plugin : Rygel.MediaServerPlugin {
+    public const string NAME = "Tracker";
+
+    private static RootContainer root;
+
+    public Plugin () {
+        if (root == null) {
+            // translators: @REALNAME@ is substituted for user's real name and it doesn't need translation.
+            root = new RootContainer (_("@REALNAME@’s media"));
+        }
+
+        base (root, Plugin.NAME, null, PluginCapabilities.UPLOAD);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-query-triplet.vala 
b/src/plugins/tracker3/rygel-tracker-query-triplet.vala
new file mode 100644
index 00000000..870dfe7d
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-query-triplet.vala
@@ -0,0 +1,106 @@
+/*
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Represents SPARQL Triplet
+ */
+public class Rygel.Tracker.QueryTriplet {
+    public string graph;
+    public string subject;
+    public string predicate;
+    public string obj;
+
+    public QueryTriplet next;
+
+    public QueryTriplet (string subject, string predicate, string obj) {
+        this.graph = null;
+        this.subject = subject;
+        this.predicate = predicate;
+        this.obj = obj;
+    }
+
+    public QueryTriplet.with_graph (string graph,
+                                    string subject,
+                                    string predicate,
+                                    string object) {
+        this.graph = graph;
+        this.subject = subject;
+        this.predicate = predicate;
+        this.obj = object;
+    }
+
+    public QueryTriplet.chain (string       subject,
+                               string       predicate,
+                               QueryTriplet next) {
+        this.subject = subject;
+        this.predicate = predicate;
+        this.next = next;
+    }
+
+    public QueryTriplet.clone (QueryTriplet triplet) {
+        this.graph = triplet.graph;
+        this.subject = triplet.subject;
+        this.predicate = triplet.predicate;
+
+        if (triplet.next != null) {
+            this.next = triplet.next;
+        } else {
+            this.obj = triplet.obj;
+        }
+    }
+
+    public static bool equal_func (QueryTriplet a, QueryTriplet b) {
+        bool chain_equal;
+
+        if (a.next != null && b.next != null) {
+            chain_equal = equal_func (a.next, b.next);
+        } else {
+            chain_equal = a.next == b.next;
+        }
+
+        return a.graph == b.graph &&
+               a.subject == b.subject &&
+               a.obj == b.obj &&
+               a.predicate == b.predicate &&
+               chain_equal;
+    }
+
+    public string to_string (bool include_subject = true) {
+        string str = "";
+
+        if (include_subject) {
+            str += " " + subject;
+        }
+
+        str += " " + predicate;
+
+        if (this.next != null) {
+            str += " [ " + this.next.to_string () + " ] ";
+        } else {
+            str += " " + this.obj;
+        }
+
+        return str;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-query-triplets.vala 
b/src/plugins/tracker3/rygel-tracker-query-triplets.vala
new file mode 100644
index 00000000..92a39de7
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-query-triplets.vala
@@ -0,0 +1,89 @@
+/*
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Represents a list of SPARQL Triplet
+ */
+public class Rygel.Tracker.QueryTriplets : ArrayList<QueryTriplet> {
+    public QueryTriplets () {
+        base ((EqualDataFunc<QueryTriplet>) QueryTriplet.equal_func);
+    }
+
+    public QueryTriplets.clone (QueryTriplets triplets) {
+        base ((EqualDataFunc<QueryTriplet>) QueryTriplet.equal_func);
+
+        foreach (var triplet in triplets) {
+            this.add (new QueryTriplet.clone (triplet));
+        }
+    }
+
+    public string serialize () {
+        string str = "";
+        var include_subject = true;
+
+        for (int i = 0; i < this.size; i++) {
+            if (this[i].graph != null && include_subject) {
+                str += "GRAPH <%s> {".printf (this[i].graph);
+            }
+
+            str += this[i].to_string (include_subject);
+
+            if (i < this.size - 1) {
+                include_subject = this[i].subject != this[i + 1].subject ||
+                                  this.check_graph_change (i);
+
+                if (include_subject) {
+                    str += " . ";
+                    // close the graph if we change the subject
+                    if (this[i].graph != null) {
+                        str += "} ";
+                    }
+                } else {
+                    str += " ; ";
+                }
+            } else {
+                // need to explicitly close the graph for the last triplet
+                if (this[i].graph != null) {
+                    str += " . } ";
+                }
+            }
+        }
+
+        return str;
+    }
+
+    public override bool add (QueryTriplet triplet) {
+        if (!this.contains (triplet)) {
+            return base.add (triplet);
+        }
+
+        return false;
+    }
+
+    private bool check_graph_change (int i) {
+        return (this[i].graph == null && this[i + 1].graph != null) ||
+               (this[i].graph != null && this[i + 1].graph == null) ||
+               (this[i].graph != this[i + 1].graph);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-query.vala b/src/plugins/tracker3/rygel-tracker-query.vala
new file mode 100644
index 00000000..8aa7ea08
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-query.vala
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ *
+ * Authors: Zeeshan Ali <zeenix gmail com>
+ *          Ivan Frade <ivan frade nokia com>
+ *          Jens Georg <jensg openismus com>
+ *          Luis de Bethencourt <luisbg collabora 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Represents Tracker SPARQL query
+ */
+public abstract class Rygel.Tracker.Query {
+    public QueryTriplets triplets;
+
+    protected Query (QueryTriplets triplets) {
+        this.triplets = triplets;
+    }
+
+    public abstract async void execute (Sparql.Connection resources)
+                                        throws Error,
+                                               IOError,
+                                               Sparql.Error,
+                                               DBusError;
+
+    // Deriving classes should override this method and complete it by
+    // adding the first part of the query
+    public virtual string to_string () {
+        return this.triplets.serialize ();
+    }
+
+    /**
+     * Convenience function to combine Query.escape_string and
+     * Regex.escape_string in one function call
+     *
+     * @param literal A string to escape
+     *
+     * @return A newly allocated string with the sparql-escaped regex-escaped
+     * version of literal. The returned string should be freed with g_free()
+     * when no longer needed.
+     */
+    public static string escape_regex (string literal) {
+        return escape_string (Regex.escape_string (literal));
+    }
+
+    /**
+     * tracker_sparql_escape_string: Escapes a string so that it can be
+     * used in a SPARQL query. Copied from Tracker project.
+     *
+     * @param literal A string to escape
+     *
+     * @return A newly-allocated string with the escaped version of
+     * literal. The returned string should be freed with g_free() when no
+     * longer needed.
+     */
+    public static string escape_string (string literal) {
+        StringBuilder str = new StringBuilder ();
+        char *p = literal;
+
+        while (*p != '\0') {
+            size_t len = Posix.strcspn ((string) p, "\t\n\r\b\f\"\\");
+            str.append_len ((string) p, (long) len);
+            p += len;
+
+            switch (*p) {
+                case '\t':
+                    str.append ("\\t");
+                    break;
+                case '\n':
+                    str.append ("\\n");
+                    break;
+                case '\r':
+                    str.append ("\\r");
+                    break;
+                case '\b':
+                    str.append ("\\b");
+                    break;
+                case '\f':
+                    str.append ("\\f");
+                    break;
+                case '"':
+                    str.append ("\\\"");
+                    break;
+                case '\\':
+                    str.append ("\\\\");
+                    break;
+                default:
+                    continue;
+            }
+
+            p++;
+        }
+
+        return str.str;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-root-container.vala 
b/src/plugins/tracker3/rygel-tracker-root-container.vala
new file mode 100644
index 00000000..88a7d3af
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-root-container.vala
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GUPnP;
+using Gee;
+
+/**
+ * Represents the root container for Tracker media content hierarchy.
+ */
+public class Rygel.Tracker.RootContainer : Rygel.SimpleContainer {
+    public RootContainer (string title) {
+        base.root (title);
+
+        if (this.get_bool_config_without_error ("share-music")) {
+            this.add_child_container (new Music ("Music", this, "Music"));
+        }
+
+        if (this.get_bool_config_without_error ("share-videos")) {
+            this.add_child_container (new Videos ("Videos", this, "Videos"));
+        }
+
+        if (this.get_bool_config_without_error ("share-pictures")) {
+            this.add_child_container (new Pictures ("Pictures",
+                                                    this,
+                                                    "Pictures"));
+        }
+    }
+
+    private bool get_bool_config_without_error (string key) {
+        var value = true;
+        var config = MetaConfig.get_default ();
+
+        try {
+            value = config.get_bool ("Tracker", key);
+        } catch (GLib.Error error) {}
+
+        return value;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-search-container.vala 
b/src/plugins/tracker3/rygel-tracker-search-container.vala
new file mode 100644
index 00000000..4310e090
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-search-container.vala
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ * Copyright (C) 2010 MediaNet Inh.
+ *
+ * Authors: Zeeshan Ali <zeenix gmail com>
+ *          Sunil Mohan Adapa <sunil medhas org>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GUPnP;
+using Gee;
+using Tracker;
+
+/**
+ * A container listing a Tracker search result.
+ */
+public class Rygel.Tracker.SearchContainer : SimpleContainer {
+    /* class-wide constants */
+    private const string MODIFIED_PROPERTY = "nfo:fileLastModified";
+
+    public SelectionQuery query;
+    public ItemFactory item_factory;
+
+    private Sparql.Connection resources;
+
+    private static HashMap<string, uint> update_id_hash;
+
+    public SearchContainer (string             id,
+                            MediaContainer     parent,
+                            string             title,
+                            ItemFactory        item_factory,
+                            QueryTriplets?     triplets = null,
+                            ArrayList<string>? filters = null) {
+        base (id, parent, title);
+
+        if (unlikely (update_id_hash == null)) {
+            update_id_hash = new HashMap<string, uint> ();
+        }
+
+        if (update_id_hash.has_key (this.id)) {
+            this.update_id = update_id_hash[this.id];
+        }
+
+        this.container_updated.connect ( (container, origin) => {
+            if (origin == this) {
+                update_id_hash[this.id] = this.update_id;
+            }
+        });
+
+        this.item_factory = item_factory;
+
+        var variables = new ArrayList<string> ();
+        variables.add (SelectionQuery.ITEM_VARIABLE);
+
+        QueryTriplets our_triplets;
+        if (triplets != null) {
+            our_triplets = triplets;
+        } else {
+            our_triplets = new QueryTriplets ();
+        }
+
+        our_triplets.add (new QueryTriplet
+                                (SelectionQuery.ITEM_VARIABLE,
+                                 "a",
+                                 item_factory.category));
+
+        var property_map = UPnPPropertyMap.get_property_map ();
+        foreach (var property in this.item_factory.properties) {
+            variables.add (property_map[property]);
+        }
+
+        var order_by = MODIFIED_PROPERTY +
+                       "(" +
+                       SelectionQuery.ITEM_VARIABLE +
+                       ")";
+
+        this.query = new SelectionQuery (variables,
+                                         our_triplets,
+                                         filters,
+                                         order_by);
+
+        try {
+            this.resources = Sparql.Connection.bus_new ("org.freedesktop.Tracker3.Miner.Files", null);
+
+            this.get_children_count.begin ();
+        } catch (Error error) {
+            critical (_("Failed to get Tracker connection: %s"), error.message);
+        }
+    }
+
+    public override async MediaObjects? get_children (uint       offset,
+                                                      uint       max_count,
+                                                      string     sort_criteria,
+                                                      Cancellable? cancellable)
+                                                      throws GLib.Error {
+        var expression = new RelationalExpression ();
+        expression.op = SearchCriteriaOp.EQ;
+        expression.operand1 = "@parentID";
+        expression.operand2 = this.id;
+
+        uint total_matches;
+
+        return yield this.execute_query (expression,
+                                         sort_criteria,
+                                         offset,
+                                         max_count,
+                                         cancellable,
+                                         out total_matches);
+    }
+
+    public async MediaObjects? execute_query (SearchExpression? expression,
+                                              string            sort_criteria,
+                                              uint              offset,
+                                              uint              max_count,
+                                              Cancellable?      cancellable,
+                                              out uint          total_matches)
+                                              throws GLib.Error {
+        var results = new MediaObjects ();
+
+        var query = this.create_query (expression as RelationalExpression,
+                                       (int) offset,
+                                       (int) max_count,
+                                       sort_criteria);
+
+        if (query != null) {
+            yield query.execute (this.resources);
+
+            /* Iterate through all items */
+            while (yield query.result.next_async ()) {
+                var id = query.result.get_string (0);
+                id = this.create_child_id_for_urn (id);
+                var uri = query.result.get_string (1);
+
+                var item = this.item_factory.create (id,
+                                                     uri,
+                                                     this,
+                                                     query.result);
+                results.add (item);
+            }
+        }
+
+        total_matches = results.size;
+
+        return results;
+    }
+
+    public override async MediaObject? find_object (string       id,
+                                                    Cancellable? cancellable)
+                                                    throws GLib.Error {
+        if (!this.is_our_child (id)) {
+            return null;
+        }
+
+        var expression = new RelationalExpression ();
+        expression.op = SearchCriteriaOp.EQ;
+        expression.operand1 = "@id";
+        expression.operand2 = id;
+
+        uint total_matches;
+        var results = yield this.execute_query (expression,
+                                                "",
+                                                0,
+                                                1,
+                                                cancellable,
+                                                out total_matches);
+        if (results.size > 0) {
+            return results[0];
+        } else {
+            return null;
+        }
+    }
+
+    public string create_child_id_for_urn (string urn) {
+        return this.id + "," + urn;
+    }
+
+    // Returns the URN and the ID of the parent this item belongs to, or null
+    // if item_id is invalid
+    protected string? get_item_info (string     item_id,
+                                     out string parent_id) {
+        var tokens = item_id.split (",", 2);
+
+        if (tokens[0] != null && tokens[1] != null) {
+            parent_id = tokens[0];
+
+            return tokens[1];
+        } else {
+            parent_id = null;
+
+            return null;
+        }
+    }
+
+    internal async void get_children_count () {
+        try {
+            var query = new SelectionQuery.clone (this.query);
+
+            query.variables = new ArrayList<string> ();
+            query.variables.add ("COUNT(" +
+                                 SelectionQuery.ITEM_VARIABLE +
+                                 ") AS ?x");
+
+            yield query.execute (this.resources);
+
+            if (query.result.next ()) {
+                this.child_count = int.parse (query.result.get_string (0));
+                this.updated ();
+            }
+
+        } catch (GLib.Error error) {
+            critical (_("Error getting item count under category “%s”: %s"),
+                      this.item_factory.category,
+                      error.message);
+
+            return;
+        }
+    }
+
+    private bool is_our_child (string id) {
+        return id.has_prefix (this.id + ",");
+    }
+
+    private SelectionQuery? create_query (RelationalExpression? expression,
+                                          int offset,
+                                          int max_count,
+                                          string sort_criteria = "") {
+        if (expression.operand1 == "upnp:class" &&
+            !this.item_factory.upnp_class.has_prefix (expression.operand2)) {
+            return null;
+        }
+
+        SelectionQuery query;
+        if (sort_criteria == null || sort_criteria == "") {
+            query = new SelectionQuery.clone (this.query);
+        } else {
+            query = create_sorted_query (sort_criteria);
+        }
+
+        if (expression.operand1 == "@parentID") {
+            if (!expression.compare_string (this.id)) {
+                return null;
+            }
+        } else if (expression.operand1 != "upnp:class") {
+            var filter = create_filter_for_child (expression);
+            if (filter != null) {
+                query.filters.insert (0, filter);
+            } else {
+                return null;
+            }
+        }
+
+        query.offset = offset;
+        query.max_count = max_count;
+
+        return query;
+    }
+
+    private SelectionQuery? create_sorted_query (string sort_criteria) {
+         var key_chain_map = UPnPPropertyMap.get_property_map ();
+         var sort_props = sort_criteria.split (",");
+         string order = "";
+         ArrayList<string> variables = new ArrayList<string> ();
+         ArrayList<string> filters = new ArrayList<string> ();
+
+         variables.add_all (this.query.variables);
+         filters.add_all (this.query.filters);
+
+         foreach (string s in sort_props) {
+             var key = key_chain_map[s.substring(1)];
+             if (key.index_of (SelectionQuery.ITEM_VARIABLE) == 0) {
+                 continue;
+             }
+
+             if (s.has_prefix("-")) {
+                 order += "DESC (" +
+                           key + ") ";
+             } else {
+                 order += key + " ";
+             }
+         }
+
+         if (order == "") {
+             order = this.query.order_by;
+         }
+
+         return new SelectionQuery (
+                                variables,
+                                new QueryTriplets.clone(this.query.triplets),
+                                filters,
+                                order);
+    }
+
+    private string? urn_to_utf8 (string urn) {
+        var urn_builder = new StringBuilder ();
+        unowned string s = urn;
+
+        for (; s.get_char () != 0; s = s.next_char ()) {
+            unichar character = s.get_char ();
+            if (!(character.iscntrl () || !character.validate ())) {
+                urn_builder.append_unichar (character);
+            }
+        }
+
+        return urn_builder.str;
+    }
+
+    private string? create_filter_for_child (RelationalExpression expression) {
+        string filter = null;
+        string variable = null;
+        string value = null;
+
+        if (expression.operand1 == "@id") {
+            variable = SelectionQuery.ITEM_VARIABLE;
+
+            string parent_id;
+
+            var urn = this.get_item_info (expression.operand2, out parent_id);
+
+            if (!urn.validate ()) {
+                urn = urn_to_utf8 (urn);
+            }
+
+            if (urn == null || parent_id == null || parent_id != this.id) {
+                return null;
+            }
+
+            urn = Query.escape_string (urn);
+
+            switch (expression.op) {
+                case SearchCriteriaOp.EQ:
+                    value = "<" + urn + ">";
+                    break;
+                case SearchCriteriaOp.CONTAINS:
+                    value = expression.operand2;
+                    break;
+            }
+        }
+
+        if (variable == null || value == null) {
+            return null;
+        }
+
+        switch (expression.op) {
+            case SearchCriteriaOp.EQ:
+                filter = variable + " = " + value;
+                break;
+            case SearchCriteriaOp.CONTAINS:
+                // We need to escape this twice for Tracker
+                var regex = Query.escape_regex (value);
+
+                filter = "regex(" + variable + ", \"" + regex + "\", \"i\")";
+                break;
+        }
+
+        return filter;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-selection-query.vala 
b/src/plugins/tracker3/rygel-tracker-selection-query.vala
new file mode 100644
index 00000000..9a0f8282
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-selection-query.vala
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010-2012 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali <zeenix gmail com>
+ *         Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Represents Tracker SPARQL Selection query
+ */
+public class Rygel.Tracker.SelectionQuery : Query {
+    public const string ITEM_VARIABLE = "?item";
+    private const string SHARED_FILTER = "(!BOUND(nmm:uPnPShared(" +
+                                         ITEM_VARIABLE + ")) ||" +
+                                         " nmm:uPnPShared(" +
+                                         ITEM_VARIABLE +
+                                         ") = true) && " +
+                                         "(BOUND(nie:url(" +
+                                         ITEM_VARIABLE + ")))";
+    private const string STRICT_SHARED_FILTER = "(BOUND(nmm:dlnaProfile(" +
+                                                ITEM_VARIABLE + ")))";
+    private const string AVAILABLE_FILTER = "(tracker:available(" +
+                                            ITEM_VARIABLE + ") = true)";
+
+    private string uri_filter;
+
+    public ArrayList<string> variables;
+    public ArrayList<string> filters;
+
+    public string order_by;
+    public int offset;
+    public int max_count;
+
+    public Sparql.Cursor result;
+
+    public SelectionQuery (ArrayList<string>  variables,
+                           QueryTriplets      triplets,
+                           ArrayList<string>? filters,
+                           string?            order_by = null,
+                           int                offset = 0,
+                           int                max_count = -1) {
+        base (triplets);
+
+        if (filters != null) {
+            this.filters = filters;
+        } else {
+            this.filters = new ArrayList<string> ();
+        }
+
+        this.variables = variables;
+        this.order_by = order_by;
+        this.offset = offset;
+        this.max_count = max_count;
+
+        ArrayList<string> uris;
+        string[] uri_filters = new string[0];
+
+        var config = MetaConfig.get_default ();
+
+        try {
+            uris = config.get_string_list ("Tracker", "only-export-from");
+        } catch (Error error) {
+            uris = new ArrayList<string> ();
+        }
+
+        var home_dir = File.new_for_path (Environment.get_home_dir ());
+        unowned string pictures_dir = Environment.get_user_special_dir
+                                        (UserDirectory.PICTURES);
+        unowned string videos_dir = Environment.get_user_special_dir
+                                        (UserDirectory.VIDEOS);
+        unowned string music_dir = Environment.get_user_special_dir
+                                        (UserDirectory.MUSIC);
+
+        foreach (var uri in uris) {
+            var file = File.new_for_commandline_arg (uri);
+            if (!file.equal (home_dir)) {
+                var actual_uri = uri;
+
+                if (pictures_dir != null) {
+                    actual_uri = actual_uri.replace ("@PICTURES@", pictures_dir);
+                }
+                if (videos_dir != null) {
+                    actual_uri = actual_uri.replace ("@VIDEOS@", videos_dir);
+                }
+                if (music_dir != null) {
+                    actual_uri = actual_uri.replace ("@MUSIC@", music_dir);
+                }
+
+                if (actual_uri.contains ("@PICTURES@") ||
+                    actual_uri.contains ("@VIDEOS@") ||
+                    actual_uri.contains ("@MUSIC@")) {
+                    continue;
+                }
+
+                // protect against special directories expanding to $HOME
+                file = File.new_for_commandline_arg (actual_uri);
+                if (file.equal (home_dir)) {
+                    continue;
+                }
+
+                uri_filters += "tracker:uri-is-descendant(\"%s\", nie:url(%s))".printf
+                                (file.get_uri (), ITEM_VARIABLE);
+            }
+        }
+
+        if (uri_filters.length != 0) {
+            this.uri_filter = "(%s)".printf (string.joinv ("||", uri_filters));
+        } else {
+            this.uri_filter = null;
+        }
+    }
+
+    public SelectionQuery.clone (SelectionQuery query) {
+        this (copy_str_list (query.variables),
+              new QueryTriplets.clone (query.triplets),
+              copy_str_list (query.filters),
+              query.order_by,
+              query.offset,
+              query.max_count);
+    }
+
+    public override async void execute (Sparql.Connection resources)
+                                        throws Error,
+                                               IOError,
+                                               Sparql.Error,
+                                               DBusError {
+        var str = this.to_string ();
+
+        critical ("Executing SPARQL query: %s", str);
+
+        result = yield resources.query_async (str);
+    }
+
+    public override string to_string () {
+        var query = "SELECT ";
+
+        foreach (var variable in this.variables) {
+            query += " " + variable;
+        }
+
+        query += " WHERE { " + base.to_string ();
+
+        var filters = new ArrayList<string> ();
+        filters.add_all (this.filters);
+        // Make sure we don't expose items that are marked not to be shared
+        filters.add (SHARED_FILTER);
+
+        // Make sure we don't expose items on removable media that isn't
+        // mounted
+        filters.add (AVAILABLE_FILTER);
+
+        // If strict sharing is enabled, only expose files that have a DLNA
+        // profile set
+        try {
+            var config = MetaConfig.get_default ();
+            if (config.get_bool ("Tracker", "strict-sharing")) {
+                filters.add (STRICT_SHARED_FILTER);
+            }
+        } catch (Error error) {};
+
+        // Limit the files to a set of folders that may have been configured
+        if (uri_filter != null) {
+            filters.add (uri_filter);
+        }
+
+        if (filters.size > 0) {
+            query += " FILTER (";
+            for (var i = 0; i < filters.size; i++) {
+                query += filters[i];
+
+                if (i < filters.size - 1) {
+                    query += " && ";
+                }
+            }
+            query += ")";
+        }
+
+        query += " }";
+
+        if (this.order_by != null) {
+            query += " ORDER BY " + order_by;
+        }
+
+        if (this.offset > 0) {
+            query += " OFFSET " + this.offset.to_string ();
+        }
+
+        if (this.max_count > 0) {
+            query += " LIMIT " + this.max_count.to_string ();
+        }
+
+        return query;
+    }
+
+    private static ArrayList<string> copy_str_list (Gee.List<string> str_list) {
+        var copy = new ArrayList<string> ();
+
+        copy.add_all (str_list);
+
+        return copy;
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-tags.vala b/src/plugins/tracker3/rygel-tracker-tags.vala
new file mode 100644
index 00000000..196a093e
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-tags.vala
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GUPnP;
+using Gee;
+
+/**
+ * Container listing all available tag labels in Tracker DB.
+ */
+public class Rygel.Tracker.Tags : MetadataMultiValues {
+    /* class-wide constants */
+    private const string TITLE = "Tags";
+
+    private const string[] KEY_CHAIN = { "nao:hasTag", "nao:prefLabel", null };
+
+    public Tags (MediaContainer parent, ItemFactory item_factory) {
+        base (parent.id + TITLE, parent, TITLE, item_factory, KEY_CHAIN);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-titles.vala b/src/plugins/tracker3/rygel-tracker-titles.vala
new file mode 100644
index 00000000..c51f7525
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-titles.vala
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * Container providing a title-based hierarchy.
+ *
+ * Under each category container, add a container providing a title-based
+ * hierarchy: One container for each unique starting character of all the titles
+ * available for the contegory in question. Something like this:
+ *
+ * Music
+ *   |
+ *   |----> Genre
+ *           |..
+ *   ^
+ *   |----> Titles
+ *           |
+ *           |---> A
+ *                 |
+ *                 |--> Alpha
+ *                 |--> alright
+ *           ^
+ *           |---> B
+ *                 |
+ *                 |--> Bravo
+ *                 |--> brave
+ *                 |..
+ *           ^
+ *   ^       |..
+ *   |..
+ */
+public class Rygel.Tracker.Titles : MetadataValues {
+    private const string[] KEY_CHAIN = { "dc:title", null };
+
+    public Titles (MediaContainer parent, ItemFactory item_factory) {
+        base (parent.id + "Titles",
+              parent,
+              _("Titles"),
+              item_factory,
+              "dc:title");
+    }
+
+    // The parent class will only create a child container for each unique
+    // title this method returns so we don't need to worry about multiple
+    // containers being created for each letter.
+    protected override string? create_title_for_value (string value) {
+        var c = value.get_char_validated ();
+
+        if (unlikely (c < 0)) {
+            return null;
+        }
+
+        return c.to_string ().up ();
+    }
+
+    protected override string create_filter (string variable, string value) {
+        var title = Query.escape_regex (this.create_title_for_value (value));
+
+        return "regex(" + variable + ", \"^" + title + "\", \"i\")";
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-upnp-property-map.vala 
b/src/plugins/tracker3/rygel-tracker-upnp-property-map.vala
new file mode 100644
index 00000000..7670b57c
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-upnp-property-map.vala
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 MediaNet Inh.
+ * Copyright (C) 2012 Jens Georg <mail jensge org>
+ *
+ * Author: Sunil Mohan Adapa <sunil medhas org>
+ *
+ * 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * A map of UPnP properties to tracker property functions, coalesces,
+ * subqueries or other custom functions
+ */
+public class Rygel.Tracker.UPnPPropertyMap : Object {
+    HashMap<string, ArrayList<string>> property_map;
+    HashMap<string, string> functions;
+    private static UPnPPropertyMap instance;
+
+    public static UPnPPropertyMap get_property_map () {
+        if (unlikely (instance == null)) {
+            instance = new UPnPPropertyMap ();
+        }
+
+        return instance;
+    }
+
+    private UPnPPropertyMap () {
+        this.property_map = new HashMap<string, ArrayList<string>> ();
+        this.functions = new HashMap<string, string> ();
+
+        // Item
+        this.add_key_chain ("res", "nie:url");
+        this.add_function ("place_holder",
+                      "tracker:coalesce((SELECT false WHERE { { %s a ?o } " +
+                      "FILTER (?o IN (nfo:FileDataObject, " +
+                      "nfo:RemoteDataObject)) }), true)");
+        this.add_key_chain ("fileName", "nfo:fileName");
+        this.add_alternative ("dc:title", "nie:title", "nfo:fileName");
+        this.add_key_chain ("dlnaProfile", "nmm:dlnaProfile");
+        this.add_alternative ("mimeType", "nmm:dlnaMime", "nie:mimeType");
+        this.add_alternative ("res@size", "nfo:fileSize", "nie:byteSize");
+        this.add_alternative ("date",
+                              "nie:contentCreated",
+                              "nfo:fileLastModified");
+
+        // Music Item
+        this.add_key_chain ("res@duration", "nfo:duration");
+        this.add_key_chain ("upnp:artist", "nmm:performer", "nmm:artistName");
+        this.add_key_chain ("dc:creator", "nmm:performer", "nmm:artistName");
+        this.add_key_chain ("upnp:album", "nmm:musicAlbum", "nie:title");
+        this.add_key_chain ("upnp:originalTrackNumber", "nmm:trackNumber");
+        this.add_key_chain ("upnp:genre", "nfo:genre");
+        this.add_key_chain ("sampleRate", "nfo:sampleRate");
+        this.add_key_chain ("upnp:nrAudioChannels", "nfo:channels");
+        this.add_key_chain ("upnp:bitsPerSample", "nfo:bitsPerSample");
+        this.add_key_chain ("upnp:bitrate", "nfo:averageBitrate");
+
+        // Picture & Video Items
+        this.add_key_chain ("width", "nfo:width");
+        this.add_key_chain ("height", "nfo:height");
+
+        this.add_key_chain ("rygel:originalVolumeNumber",
+                            "nmm:musicAlbumDisc",
+                            "nmm:setNumber");
+    }
+
+    public new string @get (string property) {
+        var str = SelectionQuery.ITEM_VARIABLE;
+
+        if (this.property_map.has_key (property)) {
+            foreach (var key in this.property_map[property]) {
+                str = key + "(" + str + ")";
+            }
+        } else if (this.functions.has_key (property)) {
+            str = this.functions[property].printf (str);
+        }
+
+        return str;
+    }
+
+    private void add_key_chain (string property, ...) {
+        var key_chain = new ArrayList<string> ();
+
+        var list = va_list ();
+        string key = list.arg ();
+
+        while (key != null) {
+            key_chain.add (key);
+
+            key = list.arg ();
+        }
+
+        this.property_map[property] = key_chain;
+    }
+
+    private void add_function (string property, string function) {
+        this.functions[property] = function;
+    }
+
+    private void add_alternative (string property, ...) {
+        var list = va_list ();
+
+        var str = new StringBuilder ("tracker:coalesce(");
+
+        string alternative = list.arg ();
+        while (alternative != null) {
+            str.append_printf ("%s(%%1$s),", alternative);
+            alternative = list.arg ();
+        }
+
+        str.truncate (str.len - 1);
+        str.append (")");
+
+        this.add_function (property, str.str);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-video-item-factory.vala 
b/src/plugins/tracker3/rygel-tracker-video-item-factory.vala
new file mode 100644
index 00000000..07aac77b
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-video-item-factory.vala
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
+ * Copyright (C) 2008-2012 Nokia Corporation.
+ * Copyright (C) 2010 MediaNet Inh.
+ *
+ * Authors: Zeeshan Ali <zeenix gmail com>
+ *          Sunil Mohan Adapa <sunil medhas org>
+ *          Jens Georg <jensg openismus 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+using Tracker;
+
+/**
+ * Tracker video item factory.
+ */
+public class Rygel.Tracker.VideoItemFactory : ItemFactory {
+    private enum VideoMetadata {
+        HEIGHT = Metadata.LAST_KEY,
+        WIDTH,
+        DURATION,
+
+        LAST_KEY
+    }
+
+    private const string CATEGORY = "nmm:Video";
+    private const string CATEGORY_IRI = "http://www.tracker-project.org/"; +
+                                        "temp/nmm#Video";
+
+    public VideoItemFactory () {
+        var upload_folder = Environment.get_user_special_dir
+                                        (UserDirectory.VIDEOS);
+        try {
+            var config = MetaConfig.get_default ();
+            upload_folder = config.get_video_upload_folder ();
+        } catch (Error error) {};
+
+        base (CATEGORY, CATEGORY_IRI, VideoItem.UPNP_CLASS, upload_folder);
+
+        // These must be in the same order as enum VideoMetadata
+        this.properties.add ("height");
+        this.properties.add ("width");
+        this.properties.add ("res@duration");
+    }
+
+    public override MediaFileItem create (string          id,
+                                          string          uri,
+                                          SearchContainer parent,
+                                          Sparql.Cursor   metadata)
+                                          throws GLib.Error {
+        var item = new VideoItem (id, parent, "");
+
+        this.set_metadata (item, uri, metadata);
+
+        return item;
+    }
+
+    protected override void set_metadata (MediaFileItem item,
+                                          string        uri,
+                                          Sparql.Cursor metadata)
+                                          throws GLib.Error {
+        base.set_metadata (item, uri, metadata);
+
+        this.set_ref_id (item, "AllVideos");
+
+        var video = item as VideoItem;
+
+        if (metadata.is_bound (VideoMetadata.WIDTH))
+            video.width = (int) metadata.get_integer (VideoMetadata.WIDTH);
+
+        if (metadata.is_bound (VideoMetadata.HEIGHT))
+            video.height = (int) metadata.get_integer (VideoMetadata.HEIGHT);
+
+        if (metadata.is_bound (VideoMetadata.DURATION))
+            video.duration = (int) metadata.get_integer
+                                        (VideoMetadata.DURATION);
+
+        base.add_resources(video);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-videos.vala b/src/plugins/tracker3/rygel-tracker-videos.vala
new file mode 100644
index 00000000..bd0741d6
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-videos.vala
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Container listing video content hierarchy.
+ */
+public class Rygel.Tracker.Videos : CategoryContainer {
+    public Videos (string id, MediaContainer parent, string title) {
+        base (id, parent, title, new VideoItemFactory ());
+
+        this.add_child_container (new Years (this, this.item_factory));
+        this.search_classes.add (VideoItem.UPNP_CLASS);
+    }
+}
diff --git a/src/plugins/tracker3/rygel-tracker-years.vala b/src/plugins/tracker3/rygel-tracker-years.vala
new file mode 100644
index 00000000..5beedf4b
--- /dev/null
+++ b/src/plugins/tracker3/rygel-tracker-years.vala
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
+ *                               <zeeshan ali nokia 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.1 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using Gee;
+
+/**
+ * Container listing content hierarchy by year of creation.
+ */
+public class Rygel.Tracker.Years : MetadataValues {
+    public Years (MediaContainer parent, ItemFactory item_factory) {
+        base (parent.id + "Year",
+              parent,
+              _("Year"),
+              item_factory,
+              "date");
+    }
+
+    protected override string? create_title_for_value (string value) {
+        return value.substring (0, 4);
+    }
+
+    protected override string create_filter (string variable, string value) {
+        var year = this.create_title_for_value (value);
+        var next_year = (int.parse (year) + 1).to_string ();
+
+        year += "-01-01T00:00:00Z";
+        next_year += "-01-01T00:00:00Z";
+
+        return variable + " > \"" + year + "\" && " +
+               variable + " < \"" + next_year + "\"";
+    }
+}
diff --git a/src/plugins/tracker3/tracker3.plugin.in b/src/plugins/tracker3/tracker3.plugin.in
new file mode 100644
index 00000000..777aa4c1
--- /dev/null
+++ b/src/plugins/tracker3/tracker3.plugin.in
@@ -0,0 +1,8 @@
+[Plugin]
+Version = @VERSION@
+Module = tracker3
+Name = Tracker3
+License = LGPL
+Conflicts = Tracker23
+Description = Share media using the Tracker meta-data store
+Copyright = Copyright © Rygel maintainers


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