2 * Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
3 * Copyright (C) 2009,2010 Nokia Corporation.
5 * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
6 * <zeeshan.ali@nokia.com>
8 * This file is part of Rygel.
10 * Rygel is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * Rygel is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 * Represents an external container.
33 public class Rygel.External.Container : Rygel.MediaContainer {
34 public MediaContainerProxy actual_container;
36 public string host_ip;
37 public string service_name;
39 private ItemFactory item_factory;
40 private ArrayList<Container> containers;
41 private Connection connection;
43 private bool searchable;
45 public Container (string id,
52 Container? parent = null) {
53 base (id, parent, title, (int) child_count);
55 this.service_name = service_name;
56 this.host_ip = host_ip;
57 this.item_factory = new ItemFactory ();
58 this.containers = new ArrayList<Container> ();
61 this.connection = DBus.Bus.get (DBus.BusType.SESSION);
62 } catch (GLib.Error err) {
63 critical ("Failed to connect to session bus: %s", err.message);
66 // Create proxy to MediaContainer iface
67 this.actual_container = this.connection.get_object (this.service_name,
69 as MediaContainerProxy;
71 this.update_container.begin (true);
74 public override async Gee.List<MediaObject>? get_children (
77 Cancellable? cancellable)
81 foreach (var object_prop in MediaObjectProxy.PROPERTIES) {
82 filter += object_prop;
85 foreach (var item_prop in MediaItemProxy.PROPERTIES) {
89 var children_props = yield this.actual_container.list_children (
94 return yield this.create_media_objects (children_props, this);
97 public override async Gee.List<MediaObject>? search (
98 SearchExpression expression,
101 out uint total_matches,
102 Cancellable? cancellable)
104 if (!this.searchable) {
105 // Backend doesn't implement search :(
106 return yield base.search (expression,
113 string[] filter = {};
114 foreach (var object_prop in MediaObjectProxy.PROPERTIES) {
115 filter += object_prop;
118 foreach (var container_prop in MediaContainerProxy.PROPERTIES) {
119 filter += container_prop;
122 foreach (var item_prop in MediaItemProxy.PROPERTIES) {
126 var ext_expression = this.translate_expression (expression);
127 var result = yield this.actual_container.search_objects (
128 ext_expression.to_string (),
132 total_matches = result.length;
134 return yield this.create_media_objects (result);
137 public override async MediaObject? find_object (string id,
138 Cancellable? cancellable)
140 MediaObject media_object = null;
142 // Create proxy to MediaObject iface
143 var actual_object = this.connection.get_object (this.service_name, id)
146 if (actual_object.object_type == "container") {
147 media_object = this.find_container_by_id (id);
149 if (media_object == null) {
150 // Not a child container, lets search in child containers then
151 foreach (var container in this.containers) {
152 media_object = yield container.find_object (id,
155 if (media_object != null) {
161 var parent_container = new DummyContainer
162 ((string) actual_object.parent,
167 var props_iface = this.connection.get_object (this.service_name, id)
170 var props = yield props_iface.get_all (MediaItemProxy.IFACE);
173 media_object = yield this.item_factory.create (
175 actual_object.object_type,
176 actual_object.display_name,
186 private async Gee.List<MediaObject> create_media_objects (
187 HashTable<string,Value?>[] all_props,
188 MediaContainer? parent
189 = null) throws GLib.Error {
190 var media_objects = new ArrayList <MediaObject> ();
192 foreach (var props in all_props) {
193 var id = props.lookup ("Path").get_string ();
194 var type = props.lookup ("Type").get_string ();
196 MediaContainer parent_container;
197 if (parent != null) {
198 parent_container = parent;
200 var parent_id = props.lookup ("Parent").get_string ();
202 parent_container = new DummyContainer (parent_id,
208 MediaObject media_object = null;
209 if (type == "container") {
210 media_object = this.find_container_by_id (id);
213 if (media_object == null) {
214 var title = props.lookup ("DisplayName").get_string ();
216 if (type == "container") {
217 var child_count = props.lookup ("ChildCount").get_uint ();
219 media_object = new DummyContainer (id,
225 media_object = yield this.item_factory.create (
236 media_objects.add (media_object);
239 return media_objects;
242 private async void refresh_child_containers () throws GLib.Error {
243 string[] filter = {};
245 foreach (var object_prop in MediaObjectProxy.PROPERTIES) {
246 filter += object_prop;
249 foreach (var container_prop in MediaContainerProxy.PROPERTIES) {
250 filter += container_prop;
253 var children_props = yield this.actual_container.list_containers (
257 this.containers.clear ();
259 foreach (var props in children_props) {
260 var path = props.lookup ("Path").get_string ();
261 var title = props.lookup ("DisplayName").get_string ();
262 var child_count = props.lookup ("ChildCount").get_uint ();
263 var searchable = props.lookup ("Searchable").get_boolean ();
265 var container = new Container (path,
273 this.containers.add (container);
277 private async void update_container (bool connect_signal = false) {
279 // Update our information about the container
280 yield this.refresh_child_containers ();
281 } catch (GLib.Error err) {
282 warning ("Failed to update information about container '%s': %s",
283 this.actual_container.get_path (),
287 // and signal the clients
290 if (connect_signal) {
291 this.actual_container.updated.connect (this.on_updated);
295 private void on_updated (MediaContainerProxy actual_container) {
296 this.update_container.begin ();
299 private MediaContainer find_container_by_id (string id) {
300 MediaContainer target = null;
302 foreach (var container in this.containers) {
303 if (container.id == id) {
313 private SearchExpression translate_expression (
314 SearchExpression upnp_expression) {
315 if (upnp_expression is RelationalExpression) {
316 var expression = upnp_expression as RelationalExpression;
317 var ext_expression = new RelationalExpression ();
318 ext_expression.op = expression.op;
319 ext_expression.operand1 = this.translate_property (
320 expression.operand1);
321 ext_expression.operand2 = expression.operand2;
323 return ext_expression;
325 var expression = upnp_expression as LogicalExpression;
326 var ext_expression = new LogicalExpression ();
328 ext_expression.op = expression.op;
329 ext_expression.operand1 = this.translate_expression (
330 expression.operand1);
331 ext_expression.operand2 = this.translate_expression (
332 expression.operand2);
334 return ext_expression;
338 public string translate_property (string property) {
345 return "DisplayName";