2 * Copyright (C) 2008 Zeeshan Ali <zeenix@gmail.com>.
3 * Copyright (C) 2008 Nokia Corporation.
5 * Author: Zeeshan Ali <zeenix@gmail.com>
7 * This file is part of Rygel.
9 * Rygel is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * Rygel is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 * Container listing possible values of a particuler Tracker metadata key.
30 public abstract class Rygel.Tracker.MetadataValues : Rygel.SimpleContainer {
31 /* class-wide constants */
32 private const string TRACKER_SERVICE = "org.freedesktop.Tracker1";
33 private const string RESOURCES_PATH = "/org/freedesktop/Tracker1/Resources";
34 private const string ITEM_VARIABLE = "?item";
36 private ItemFactory item_factory;
38 // In tracker 0.7, we might don't get values of keys in place so you need a
39 // chain of keys to reach to final destination. For instances:
40 // nmm:Performer -> nmm:artistName
41 public string[] key_chain;
43 private string child_class;
45 private ResourcesIface resources;
46 private ResourcesClassIface resources_class;
48 public MetadataValues (string id,
49 MediaContainer parent,
51 ItemFactory item_factory,
53 string? child_class = null) {
54 base (id, parent, title);
56 this.item_factory = item_factory;
57 this.key_chain = key_chain;
58 this.child_class = child_class;
61 this.create_proxies ();
62 } catch (IOError error) {
63 critical (_("Failed to connect to session bus: %s"), error.message);
68 this.fetch_metadata_values.begin ();
70 this.hook_to_changes ();
73 private async void fetch_metadata_values () {
74 // First thing, clear the existing hierarchy, if any
78 var triplets = new QueryTriplets ();
80 // All variables used in the query
81 var num_keys = this.key_chain.length - 1;
82 var variables = new string[num_keys];
83 for (i = 0; i < num_keys; i++) {
84 variables[i] = "?" + key_chain[i].replace (":", "_");
88 subject = ITEM_VARIABLE;
90 subject = variables[i - 1];
93 triplets.add (new QueryTriplet (subject,
98 triplets.insert (0, new QueryTriplet (ITEM_VARIABLE,
100 this.item_factory.category));
102 // Variables to select from query
103 var selected = new ArrayList<string> ();
104 // Last variable is the only thing we are interested in the result
105 var last_variable = variables[num_keys - 1];
106 selected.add ("DISTINCT " + last_variable);
108 var query = new SelectionQuery (selected,
114 yield query.execute (this.resources);
115 } catch (IOError error) {
116 critical (_("Error getting all values for '%s': %s"),
117 string.joinv (" -> ", this.key_chain),
123 /* Iterate through all the values */
124 for (i = 0; i < query.result.length[0]; i++) {
125 string value = query.result[i, 0];
131 var title = this.create_title_for_value (value);
132 var id = this.create_id_for_title (title);
133 if (!this.is_child_id_unique (id)) {
137 // The child container can use the same triplets we used in our
139 var child_triplets = new QueryTriplets.clone (triplets);
141 // However we constrain the object of our last triplet.
142 var filters = new ArrayList<string> ();
143 var filter = this.create_filter (child_triplets.last ().obj, value);
144 filters.add (filter);
146 var container = new SearchContainer (id,
152 if (this.child_class != null) {
153 container.upnp_class = child_class;
156 this.add_child (container);
162 public override async MediaObject? find_object (string id,
163 Cancellable? cancellable)
165 if (this.is_our_child (id)) {
166 return yield base.find_object (id, cancellable);
172 protected virtual string create_id_for_title (string title) {
173 return this.id + ":" + title;
176 protected virtual string create_title_for_value (string value) {
180 protected virtual string create_filter (string variable, string value) {
181 return variable + " = \"" + value + "\"";
184 private bool is_our_child (string id) {
185 return id.has_prefix (this.id + ":");
188 private void create_proxies () throws IOError {
189 this.resources = Bus.get_proxy_sync (BusType.SESSION,
192 this.resources_class = Bus.get_proxy_sync (
195 this.item_factory.resources_class_path);
198 private void hook_to_changes () {
199 // For any changes in subjects, just recreate hierarchy
200 this.resources_class.subjects_added.connect ((subjects) => {
201 this.fetch_metadata_values.begin ();
203 this.resources_class.subjects_removed.connect ((subjects) => {
204 this.fetch_metadata_values.begin ();
206 this.resources_class.subjects_changed.connect ((before, after) => {
207 this.fetch_metadata_values.begin ();
211 private bool is_child_id_unique (string child_id) {
214 foreach (var child in this.children) {
215 if (child.id == child_id) {