[rygel] external: Port to MediaServer2 spec
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] external: Port to MediaServer2 spec
- Date: Thu, 17 Jun 2010 17:44:14 +0000 (UTC)
commit 55e205268e06a6bd919786bcb91a443f032bb0e6
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date: Fri Jun 11 17:34:12 2010 +0300
external: Port to MediaServer2 spec
New spec available at: http://live.gnome.org/Rygel/MediaServer2Spec
src/plugins/external/Makefile.am | 1 +
src/plugins/external/rygel-external-container.vala | 212 +++++++++++--------
.../external/rygel-external-content-dir.vala | 19 ++-
.../external/rygel-external-dummy-container.vala | 48 +++++
.../external/rygel-external-interfaces.vala | 53 +++++-
.../external/rygel-external-item-factory.vala | 98 ++++------
.../external/rygel-external-plugin-factory.vala | 8 +-
7 files changed, 273 insertions(+), 166 deletions(-)
---
diff --git a/src/plugins/external/Makefile.am b/src/plugins/external/Makefile.am
index f09068e..d031f6e 100644
--- a/src/plugins/external/Makefile.am
+++ b/src/plugins/external/Makefile.am
@@ -19,6 +19,7 @@ AM_CFLAGS = $(LIBGUPNP_CFLAGS) \
librygel_external_la_SOURCES = rygel-external-content-dir.vala \
rygel-external-container.vala \
+ rygel-external-dummy-container.vala \
rygel-external-item-factory.vala \
rygel-external-thumbnail-factory.vala \
rygel-external-plugin.vala \
diff --git a/src/plugins/external/rygel-external-container.vala b/src/plugins/external/rygel-external-container.vala
index 4d7acfb..b999c23 100644
--- a/src/plugins/external/rygel-external-container.vala
+++ b/src/plugins/external/rygel-external-container.vala
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
- * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2009,2010 Nokia Corporation.
*
* Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
* <zeeshan ali nokia com>
@@ -33,41 +33,38 @@ public class Rygel.ExternalContainer : Rygel.MediaContainer {
public ExternalMediaContainer actual_container;
public string host_ip;
-
public string service_name;
- private string object_path;
+ private ExternalItemFactory item_factory;
private ArrayList<ExternalContainer> containers;
- public ExternalContainer (string id,
- string service_name,
- string object_path,
- string host_ip,
- ExternalContainer? parent) {
- base (id, parent, "Unknown", 0);
+ private bool searchable;
+
+ public ExternalContainer (string id,
+ string service_name,
+ string host_ip,
+ ExternalMediaContainer actual_container,
+ ExternalContainer? parent = null) {
+ base (id,
+ parent,
+ actual_container.display_name,
+ (int) actual_container.child_count);
this.service_name = service_name;
- this.object_path = object_path;
this.host_ip = host_ip;
+ this.actual_container = actual_container;
+ this.searchable = actual_container.searchable;
+ this.item_factory = new ExternalItemFactory ();
this.containers = new ArrayList<ExternalContainer> ();
try {
- DBus.Connection connection = DBus.Bus.get (DBus.BusType.SESSION);
-
- // Create proxy to MediaContainer iface
- this.actual_container = connection.get_object (
- service_name,
- object_path)
- as ExternalMediaContainer;
- this.title = this.actual_container.display_name;
-
this.update_container ();
this.actual_container.updated += this.on_updated;
} catch (GLib.Error err) {
- critical ("Failed to fetch information about container '%s': %s\n",
- this.id,
+ critical ("Failed to fetch information about container '%s': %s",
+ actual_container.get_path (),
err.message);
}
}
@@ -77,30 +74,22 @@ public class Rygel.ExternalContainer : Rygel.MediaContainer {
uint max_count,
Cancellable? cancellable)
throws GLib.Error {
- var media_objects = new ArrayList <MediaObject> ();
+ string[] filter = {};
- // First add the child containers
- media_objects.add_all (this.containers);
-
- // Then get and add the child items
- var obj_paths = this.actual_container.items;
- var factory = new ExternalItemFactory ();
- foreach (var obj_path in obj_paths) {
- try {
- var item = yield factory.create_for_path (obj_path, this);
-
- media_objects.add (item);
- } catch (GLib.Error err) {
- warning ("Error initializable item at '%s': %s. Ignoring..",
- obj_path,
- err.message);
- }
+ foreach (var object_prop in ExternalMediaObject.PROPERTIES) {
+ filter += object_prop;
}
- uint stop = offset + max_count;
- stop = stop.clamp (0, media_objects.size);
+ foreach (var item_prop in ExternalMediaItem.PROPERTIES) {
+ filter += item_prop;
+ }
- return media_objects.slice ((int) offset, (int) stop);
+ var children_props = yield this.actual_container.list_children (
+ offset,
+ max_count,
+ filter);
+
+ return yield this.create_media_objects (children_props, this);
}
public override async Gee.List<MediaObject>? search (
@@ -110,10 +99,8 @@ public class Rygel.ExternalContainer : Rygel.MediaContainer {
out uint total_matches,
Cancellable? cancellable)
throws GLib.Error {
- var results = new ArrayList<MediaObject> ();
-
- /* We only deal with relational expression */
- if (expression == null || !(expression is RelationalExpression)) {
+ if (!this.searchable) {
+ // Backend doesn't implement search :(
return yield base.search (expression,
offset,
max_count,
@@ -121,68 +108,101 @@ public class Rygel.ExternalContainer : Rygel.MediaContainer {
cancellable);
}
- var rel_expression = expression as RelationalExpression;
- var id = rel_expression.operand2;
-
- /* We only deal with search for a particular item */
- if (rel_expression.operand1 != "@id" ||
- rel_expression.op != SearchCriteriaOp.EQ ||
- !is_direct_child (id)) {
- return yield base.search (expression,
- offset,
- max_count,
- out total_matches,
- cancellable);
+ string[] filter = {};
+ foreach (var object_prop in ExternalMediaObject.PROPERTIES) {
+ filter += object_prop;
}
- var factory = new ExternalItemFactory ();
+ foreach (var container_prop in ExternalMediaContainer.PROPERTIES) {
+ filter += container_prop;
+ }
- if (ExternalItemFactory.id_valid (id)) {
- var media_object = yield factory.create_for_id (id, this);
- results.add (media_object);
- } else {
- foreach (var container in this.containers) {
- if (container.id == id) {
- results.add (container);
- }
- }
+ foreach (var item_prop in ExternalMediaItem.PROPERTIES) {
+ filter += item_prop;
}
- total_matches = results.size;
+ var result = yield this.actual_container.search_objects (
+ expression.to_string (),
+ offset,
+ max_count,
+ filter);
+ total_matches = result.length;
- return results;
+ return yield this.create_media_objects (result);
}
- private bool is_direct_child (string id) {
- if (ExternalItemFactory.id_valid (id)) {
- return true;
- } else {
- foreach (var container in this.containers) {
- if (container.id == id) {
- return true;
+ private async Gee.List<MediaObject> create_media_objects (
+ HashTable<string,Value?>[] all_props,
+ MediaContainer? parent
+ = null) throws GLib.Error {
+ var media_objects = new ArrayList <MediaObject> ();
+
+ foreach (var props in all_props) {
+ var id = props.lookup ("Path").get_string ();
+ var type = props.lookup ("Type").get_string ();
+
+ MediaContainer parent_container;
+ if (parent != null) {
+ parent_container = parent;
+ } else {
+ parent_container = new ExternalDummyContainer (id,
+ "LaLaLa",
+ 0,
+ null);
+ }
+
+ MediaObject media_object = null;
+ if (type == "container") {
+ media_object = this.find_container_by_id (id);
+ }
+
+ if (media_object == null) {
+ if (type == "container") {
+ var title = props.lookup ("DisplayName").get_string ();
+ var child_count = props.lookup ("ChildCount").get_uint ();
+
+ media_object = new ExternalDummyContainer (
+ id,
+ title,
+ child_count,
+ parent_container);
+ } else {
+ // Its an item then
+ media_object = yield this.item_factory.create (
+ id,
+ props,
+ this.service_name,
+ this.host_ip,
+ parent_container);
}
}
- return false;
+ media_objects.add (media_object);
}
+
+ return media_objects;
}
private void update_container () throws GLib.Error {
this.containers.clear ();
- var obj_paths = this.actual_container.containers;
- foreach (var obj_path in obj_paths) {
- var container = new ExternalContainer (
- "container:" + (string) obj_path,
+ var connection = DBus.Bus.get (DBus.BusType.SESSION);
+
+ var container_paths = this.actual_container.containers;
+ foreach (var container_path in container_paths) {
+ // Create proxy to MediaContainer iface
+ var actual_container = connection.get_object (
this.service_name,
- obj_path,
- this.host_ip,
- this);
+ container_path)
+ as ExternalMediaContainer;
+
+ var container = new ExternalContainer (container_path,
+ this.service_name,
+ this.host_ip,
+ actual_container,
+ this);
this.containers.add (container);
}
-
- this.child_count = this.containers.size +
- (int) this.actual_container.item_count;
}
private void on_updated (ExternalMediaContainer actual_container) {
@@ -190,13 +210,27 @@ public class Rygel.ExternalContainer : Rygel.MediaContainer {
// Update our information about the container
this.update_container ();
} catch (GLib.Error err) {
- warning ("Failed to update information about container '%s': %s\n",
- this.id,
+ warning ("Failed to update information about container '%s': %s",
+ this.actual_container.get_path (),
err.message);
}
// and signal the clients
this.updated ();
}
+
+ private MediaContainer find_container_by_id (string id) {
+ MediaContainer target = null;
+
+ foreach (var container in this.containers) {
+ if (container.id == id) {
+ target = container;
+
+ break;
+ }
+ }
+
+ return target;
+ }
}
diff --git a/src/plugins/external/rygel-external-content-dir.vala b/src/plugins/external/rygel-external-content-dir.vala
index b3528da..c58f328 100644
--- a/src/plugins/external/rygel-external-content-dir.vala
+++ b/src/plugins/external/rygel-external-content-dir.vala
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
- * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2009,2010 Nokia Corporation.
*
* Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
* <zeeshan ali nokia com>
@@ -34,11 +34,24 @@ public class Rygel.ExternalContentDir : Rygel.ContentDirectory {
public override MediaContainer? create_root_container () {
var plugin = (ExternalPlugin) this.root_device.resource_factory;
+ Connection connection;
+
+ try {
+ connection = Bus.get (DBus.BusType.SESSION);
+ } catch (DBus.Error err) {
+ // By this time plugin should have successfully accessed the
+ // the session bus, so this in theory can not fail.
+ assert_not_reached ();
+ }
+
+ var actual_container = connection.get_object (plugin.service_name,
+ plugin.root_object)
+ as ExternalMediaContainer;
+
return new ExternalContainer ("0",
plugin.service_name,
- plugin.root_object,
this.context.host_ip,
- null);
+ actual_container);
}
}
diff --git a/src/plugins/external/rygel-external-dummy-container.vala b/src/plugins/external/rygel-external-dummy-container.vala
new file mode 100644
index 0000000..397210a
--- /dev/null
+++ b/src/plugins/external/rygel-external-dummy-container.vala
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009,2010 Jens Georg <mail jensge org>.
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Author: Jens Georg <mail jensge org>
+ * 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 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Rygel;
+using Gee;
+
+/**
+ * This is a dummy container used to satisfy rygel when creating objects for
+ * search resuts.
+ */
+internal class Rygel.ExternalDummyContainer : MediaContainer {
+ public ExternalDummyContainer (string id,
+ string title,
+ uint child_coult,
+ MediaContainer? parent) {
+ base (id, parent, title, child_count);
+ }
+
+ public override async Gee.List<MediaObject>? get_children (
+ uint offset,
+ uint max_count,
+ Cancellable? cancellable)
+ throws Error {
+ return new ArrayList<MediaObject> ();
+ }
+}
diff --git a/src/plugins/external/rygel-external-interfaces.vala b/src/plugins/external/rygel-external-interfaces.vala
index 1e5b4f7..31994e9 100644
--- a/src/plugins/external/rygel-external-interfaces.vala
+++ b/src/plugins/external/rygel-external-interfaces.vala
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2009,2010 Nokia Corporation.
*
* Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
* <zeeshan ali nokia com>
@@ -23,34 +23,73 @@
using DBus;
-[DBus (name = "org.gnome.UPnP.MediaObject1")]
+[DBus (name = "org.gnome.UPnP.MediaObject2")]
public interface Rygel.ExternalMediaObject : DBus.Object {
+ public static const string[] PROPERTIES = { "Parent",
+ "Type",
+ "Path",
+ "DisplayName" };
+
public abstract ObjectPath parent { owned get; set; }
public abstract string display_name { owned get; set; }
+ [DBus (name = "Type")]
+ public abstract string object_type { owned get; set; }
}
-[DBus (name = "org.gnome.UPnP.MediaContainer1")]
+[DBus (name = "org.gnome.UPnP.MediaContainer2")]
public interface Rygel.ExternalMediaContainer : DBus.Object,
ExternalMediaObject {
+ public static const string[] PROPERTIES = { "ChildCount" };
+
public abstract signal void updated ();
- public abstract ObjectPath[] items { owned get; set; }
+ //public abstract ObjectPath[] items { owned get; set; }
public abstract ObjectPath[] containers { owned get; set; }
+ public abstract uint child_count { get; set; }
public abstract uint item_count { get; set; }
public abstract uint container_count { get; set; }
+ public abstract bool searchable { get; set; }
+
+ public abstract async HashTable<string,Value?>[] list_children (
+ uint offset,
+ uint max_count,
+ string[] filter) throws DBus.Error;
// Optional API
+ public abstract async HashTable<string,Value?>[] search_objects (
+ string query,
+ uint offset,
+ uint max_count,
+ string[] filter) throws DBus.Error;
+
public abstract ObjectPath icon { owned get; set; }
}
-[DBus (name = "org.gnome.UPnP.MediaItem1")]
+[DBus (name = "org.gnome.UPnP.MediaItem2")]
public interface Rygel.ExternalMediaItem : DBus.Object, ExternalMediaObject {
+ public static const string[] PROPERTIES = { "URLs",
+ "MIMEType",
+ "DLNAProfile",
+ "Size",
+ "Artist",
+ "Album",
+ "Date",
+ "Duration",
+ "Bitrate",
+ "SampleRate",
+ "BitsPerSample",
+ "Width",
+ "Height",
+ "ColorDepth",
+ "PixelWidth",
+ "PixelHeight",
+ "Thumbnail",
+ "AlbumArt" };
+
[DBus (name = "URLs")]
public abstract string[] urls { owned get; set; }
public abstract string mime_type { owned get; set; }
- [DBus (name = "Type")]
- public abstract string media_type { owned get; set; }
// Optional API
public abstract int size { get; set; }
diff --git a/src/plugins/external/rygel-external-item-factory.vala b/src/plugins/external/rygel-external-item-factory.vala
index 97b8d00..ce467c0 100644
--- a/src/plugins/external/rygel-external-item-factory.vala
+++ b/src/plugins/external/rygel-external-item-factory.vala
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
- * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2009,2010 Nokia Corporation.
*
* Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
* <zeeshan ali nokia com>
@@ -30,69 +30,45 @@ using FreeDesktop;
* Creates item for external plugins.
*/
public class Rygel.ExternalItemFactory {
- private static string OBJECT_IFACE = "org.gnome.UPnP.MediaObject1";
- private static string ITEM_IFACE = "org.gnome.UPnP.MediaItem1";
-
- public async MediaItem create_for_path (string object_path,
- ExternalContainer parent)
- throws GLib.Error {
- return yield this.create ("item:" + object_path, object_path, parent);
- }
-
- public async MediaItem create_for_id (string id,
- ExternalContainer parent)
- throws GLib.Error {
- var object_path = id.str ("/");
- assert (object_path != null);
-
- return yield this.create (id, object_path, parent);
- }
-
- private async MediaItem create (string id,
- string object_path,
- ExternalContainer parent)
- throws GLib.Error {
- DBus.Connection connection = DBus.Bus.get (DBus.BusType.SESSION);
-
- var props = connection.get_object (parent.service_name,
- object_path)
- as Properties;
-
- var object_props = yield props.get_all (OBJECT_IFACE);
- var item_props = yield props.get_all (ITEM_IFACE);
+ public async MediaItem create (string id,
+ HashTable<string,Value?> props,
+ string service_name,
+ string host_ip,
+ MediaContainer parent)
+ throws GLib.Error {
+ var connection = DBus.Bus.get (DBus.BusType.SESSION);
var item = new MediaItem (id,
parent,
"Unknown", /* Title Unknown atm */
"Unknown"); /* UPnP Class Unknown atm */
- var value = object_props.lookup ("DisplayName");
+ var value = props.lookup ("DisplayName");
item.title = value.get_string ();
- value = item_props.lookup ("Type");
+ value = props.lookup ("Type");
string type = value.get_string ();
- if (type == "audio") {
+ if (type.has_prefix ("audio")) {
item.upnp_class = MediaItem.AUDIO_CLASS;
- } else if (type == "music") {
+ } else if (type.has_prefix ("music")) {
item.upnp_class = MediaItem.MUSIC_CLASS;
- } else if (type == "video") {
+ } else if (type.has_prefix ("video")) {
item.upnp_class = MediaItem.VIDEO_CLASS;
} else {
item.upnp_class = MediaItem.IMAGE_CLASS;
}
- value = item_props.lookup ("MIMEType");
+ value = props.lookup ("MIMEType");
item.mime_type = value.get_string ();
// FIXME: Get this value through the props until bug#602003 is fixed
- // value = item_props.lookup ("URLs");
- var item_iface = connection.get_object (parent.service_name,
- object_path)
- as ExternalMediaItem;
+ // value = props.lookup ("URLs");
+ var item_iface = connection.get_object (service_name, id)
+ as ExternalMediaItem;
string[] uris = item_iface.urls;
for (var i = 0; uris[i] != null; i++) {
- var tmp = uris[i].replace ("@ADDRESS@", parent.host_ip);
+ var tmp = uris[i].replace ("@ADDRESS@", host_ip);
item.add_uri (tmp, null);
}
@@ -105,94 +81,90 @@ public class Rygel.ExternalItemFactory {
// MediaItem1.AlbumArt
//
- value = item_props.lookup ("DLNAProfile");
+ value = props.lookup ("DLNAProfile");
if (value != null) {
item.dlna_profile = value.get_string ();
}
- value = item_props.lookup ("Size");
+ value = props.lookup ("Size");
if (value != null) {
item.size = value.get_int ();
}
- value = item_props.lookup ("Artist");
+ value = props.lookup ("Artist");
if (value != null) {
item.author = value.get_string ();
}
- value = item_props.lookup ("Album");
+ value = props.lookup ("Album");
if (value != null) {
item.album = value.get_string ();
}
- value = item_props.lookup ("Date");
+ value = props.lookup ("Date");
if (value != null) {
item.date = value.get_string ();
}
// Properties specific to video and audio/music
- value = item_props.lookup ("Duration");
+ value = props.lookup ("Duration");
if (value != null) {
item.duration = value.get_int ();
}
- value = item_props.lookup ("Bitrate");
+ value = props.lookup ("Bitrate");
if (value != null) {
item.bitrate = value.get_int ();
}
- value = item_props.lookup ("SampleRate");
+ value = props.lookup ("SampleRate");
if (value != null) {
item.sample_freq = value.get_int ();
}
- value = item_props.lookup ("BitsPerSample");
+ value = props.lookup ("BitsPerSample");
if (value != null) {
item.bits_per_sample = value.get_int ();
}
// Properties specific to video and image
- value = item_props.lookup ("Width");
+ value = props.lookup ("Width");
if (value != null) {
item.width = value.get_int ();
}
- value = item_props.lookup ("Height");
+ value = props.lookup ("Height");
if (value != null) {
item.height = value.get_int ();
}
- value = item_props.lookup ("ColorDepth");
+ value = props.lookup ("ColorDepth");
if (value != null) {
item.color_depth = value.get_int ();
}
- value = item_props.lookup ("PixelWidth");
+ value = props.lookup ("PixelWidth");
if (value != null) {
item.pixel_width = value.get_int ();
}
- value = item_props.lookup ("PixelHeight");
+ value = props.lookup ("PixelHeight");
if (value != null) {
item.pixel_height = value.get_int ();
}
- value = item_props.lookup ("Thumbnail");
+ value = props.lookup ("Thumbnail");
if (value != null) {
var factory = new ExternalThumbnailFactory ();
var thumbnail = yield factory.create (value.get_string (),
- parent.service_name,
- parent.host_ip);
+ service_name,
+ host_ip);
item.thumbnails.add (thumbnail);
}
return item;
}
-
- public static bool id_valid (string id) {
- return id.has_prefix ("item:/");
- }
}
diff --git a/src/plugins/external/rygel-external-plugin-factory.vala b/src/plugins/external/rygel-external-plugin-factory.vala
index 40ef41b..d7da3ce 100644
--- a/src/plugins/external/rygel-external-plugin-factory.vala
+++ b/src/plugins/external/rygel-external-plugin-factory.vala
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009 Zeeshan Ali (Khattak) <zeeshanak gnome org>.
- * Copyright (C) 2009 Nokia Corporation.
+ * Copyright (C) 2009,2010 Nokia Corporation.
*
* Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
* <zeeshan ali nokia com>
@@ -43,10 +43,10 @@ public class Rygel.ExternalPluginFactory {
private const string DBUS_SERVICE = "org.freedesktop.DBus";
private const string DBUS_OBJECT = "/org/freedesktop/DBus";
- private static string OBJECT_IFACE = "org.gnome.UPnP.MediaObject1";
- private static string CONTAINER_IFACE = "org.gnome.UPnP.MediaContainer1";
+ private static string OBJECT_IFACE = "org.gnome.UPnP.MediaObject2";
+ private static string CONTAINER_IFACE = "org.gnome.UPnP.MediaContainer2";
- private const string SERVICE_PREFIX = "org.gnome.UPnP.MediaServer1.";
+ private const string SERVICE_PREFIX = "org.gnome.UPnP.MediaServer2.";
DBusObject dbus_obj;
DBus.Connection connection;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]