[rygel: 2/2] server,media-export: Allow container creation
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel: 2/2] server,media-export: Allow container creation
- Date: Thu, 28 Feb 2013 10:11:11 +0000 (UTC)
commit 6dd23416cdbe9de3cf84320ef9b0a4f333a252f7
Author: Jens Georg <jensg openismus com>
Date: Wed Feb 20 14:27:51 2013 +0100
server,media-export: Allow container creation
https://bugzilla.gnome.org/show_bug.cgi?id=694155
po/POTFILES.in | 2 +-
po/POTFILES.skip | 4 +-
src/librygel-server/filelist.am | 4 +-
src/librygel-server/rygel-content-directory.vala | 2 +-
src/librygel-server/rygel-http-post.vala | 9 +-
src/librygel-server/rygel-import-resource.vala | 4 +-
src/librygel-server/rygel-item-removal-queue.vala | 89 ------
src/librygel-server/rygel-media-container.vala | 1 +
...item-creator.vala => rygel-object-creator.vala} | 311 +++++++++++++-------
.../rygel-object-removal-queue.vala | 95 ++++++
src/librygel-server/rygel-writable-container.vala | 30 ++-
.../rygel-media-export-writable-db-container.vala | 31 ++
.../rygel-tracker-category-all-container.vala | 10 +
tests/Makefile.am | 18 +-
...or.vala => rygel-http-seek_object-creator.vala} | 0
tests/rygel-item-creator.vala | 1 -
...or-test.vala => rygel-object-creator-test.vala} | 70 +++--
tests/rygel-object-creator.vala | 1 +
...r.vala => rygel-serializer_object-creator.vala} | 0
...ala => rygel-state-machine_object-creator.vala} | 0
20 files changed, 437 insertions(+), 245 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cf6e579..5e7a9bd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -100,7 +100,7 @@ src/librygel-server/rygel-http-time-seek.vala
src/librygel-server/rygel-http-transcode-handler.vala
src/librygel-server/rygel-image-item.vala
src/librygel-server/rygel-import-resource.vala
-src/librygel-server/rygel-item-creator.vala
+src/librygel-server/rygel-object-creator.vala
src/librygel-server/rygel-item-destroyer.vala
src/librygel-server/rygel-item-updater.vala
src/librygel-server/rygel-logical-expression.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 32c60c1..a05223c 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -68,9 +68,9 @@ src/librygel-server/rygel-http-time-seek.c
src/librygel-server/rygel-http-transcode-handler.c
src/librygel-server/rygel-image-item.c
src/librygel-server/rygel-import-resource.c
-src/librygel-server/rygel-item-creator.c
+src/librygel-server/rygel-object-creator.c
src/librygel-server/rygel-item-destroyer.c
-src/librygel-server/rygel-item-removal-queue.c
+src/librygel-server/rygel-object-removal-queue.c
src/librygel-server/rygel-item-updater.c
src/librygel-server/rygel-last-change.c
src/librygel-server/rygel-last-change-entry.c
diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am
index d5b1c75..71d05c6 100644
--- a/src/librygel-server/filelist.am
+++ b/src/librygel-server/filelist.am
@@ -51,10 +51,10 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \
rygel-http-time-seek.vala \
rygel-http-transcode-handler.vala \
rygel-import-resource.vala \
- rygel-item-creator.vala \
+ rygel-object-creator.vala \
rygel-item-destroyer.vala \
rygel-item-updater.vala \
- rygel-item-removal-queue.vala \
+ rygel-object-removal-queue.vala \
rygel-last-change-entry.vala \
rygel-last-change-obj-add.vala \
rygel-last-change-obj-del.vala \
diff --git a/src/librygel-server/rygel-content-directory.vala
b/src/librygel-server/rygel-content-directory.vala
index 809297e..ecef0f4 100644
--- a/src/librygel-server/rygel-content-directory.vala
+++ b/src/librygel-server/rygel-content-directory.vala
@@ -203,7 +203,7 @@ internal class Rygel.ContentDirectory: Service {
/* CreateObject action implementation */
private void create_object_cb (Service content_dir,
ServiceAction action) {
- var creator = new ItemCreator (this, action);
+ var creator = new ObjectCreator (this, action);
creator.run.begin ();
}
diff --git a/src/librygel-server/rygel-http-post.vala b/src/librygel-server/rygel-http-post.vala
index 1fe63e3..7a0a34d 100644
--- a/src/librygel-server/rygel-http-post.vala
+++ b/src/librygel-server/rygel-http-post.vala
@@ -45,14 +45,13 @@ internal class Rygel.HTTPPost : HTTPRequest {
}
protected override async void handle () throws Error {
- var queue = ItemRemovalQueue.get_default ();
- queue.dequeue (this.object as MediaItem);
+ var queue = ObjectRemovalQueue.get_default ();
+ queue.dequeue (this.object);
try {
yield this.handle_real ();
} catch (Error error) {
- yield queue.remove_now (this.object as MediaItem,
- this.cancellable);
+ yield queue.remove_now (this.object, this.cancellable);
throw error;
}
@@ -218,7 +217,7 @@ internal class Rygel.HTTPPost : HTTPRequest {
}
private async void remove_item () {
- var queue = ItemRemovalQueue.get_default ();
+ var queue = ObjectRemovalQueue.get_default ();
yield queue.remove_now (this.object as MediaItem, null);
}
diff --git a/src/librygel-server/rygel-import-resource.vala b/src/librygel-server/rygel-import-resource.vala
index 1653fdb..06a01f4 100644
--- a/src/librygel-server/rygel-import-resource.vala
+++ b/src/librygel-server/rygel-import-resource.vala
@@ -139,11 +139,9 @@ internal class Rygel.ImportResource : GLib.Object, Rygel.StateMachine {
return;
}
- var queue = ItemRemovalQueue.get_default ();
+ var queue = ObjectRemovalQueue.get_default ();
queue.dequeue (this.item);
-
-
try {
var source_file = File.new_for_uri (this.item.uris[0]);
this.output_stream = yield source_file.replace_async (null,
diff --git a/src/librygel-server/rygel-media-container.vala b/src/librygel-server/rygel-media-container.vala
index c1081b9..99094da 100644
--- a/src/librygel-server/rygel-media-container.vala
+++ b/src/librygel-server/rygel-media-container.vala
@@ -67,6 +67,7 @@ public abstract class Rygel.MediaContainer : MediaObject {
public const string MUSIC_ALBUM = UPNP_CLASS + ".album.musicAlbum";
public const string MUSIC_ARTIST = UPNP_CLASS + ".person.musicArtist";
public const string MUSIC_GENRE = UPNP_CLASS + ".genre.musicGenre";
+ public const string PLAYLIST = UPNP_CLASS + ".playlistContainer";
private const string DEFAULT_SORT_CRITERIA = "+upnp:class,+dc:title";
public const string ALBUM_SORT_CRITERIA = "+upnp:class," +
diff --git a/src/librygel-server/rygel-item-creator.vala b/src/librygel-server/rygel-object-creator.vala
similarity index 64%
rename from src/librygel-server/rygel-item-creator.vala
rename to src/librygel-server/rygel-object-creator.vala
index e33b6e7..2eedfe9 100644
--- a/src/librygel-server/rygel-item-creator.vala
+++ b/src/librygel-server/rygel-object-creator.vala
@@ -25,9 +25,69 @@
using GUPnP;
/**
+ * Dummy implementation of Rygel.MediaContainer to pass on to
+ * Rygel.WritableContianer for creation.
+ */
+private class Rygel.BaseMediaContainer : MediaContainer {
+ /**
+ * Create a media container with the specified details.
+ *
+ * @param id See the id property of the #RygelMediaObject class.
+ * @param parent The parent container, if any.
+ * @param title See the title property of the #RygelMediaObject class.
+ * @param child_count The initially-known number of child items.
+ */
+ public BaseMediaContainer (string id,
+ MediaContainer? parent,
+ string title,
+ int child_count) {
+ Object (id : id,
+ parent : parent,
+ title : title,
+ child_count : child_count);
+ }
+
+ /**
+ * Fetches the list of media objects directly under this container.
+ *
+ * @param offset zero-based index of the first item to return
+ * @param max_count maximum number of objects to return
+ * @param sort_criteria sorting order of objects to return
+ * @param cancellable optional cancellable for this operation
+ *
+ * @return A list of media objects.
+ */
+ public override async MediaObjects? get_children
+ (uint offset,
+ uint max_count,
+ string sort_criteria,
+ Cancellable? cancellable)
+ throws Error {
+ return null;
+ }
+
+ /**
+ * Recursively searches this container for a media object with the given ID.
+ *
+ * @param id ID of the media object to search for
+ * @param cancellable optional cancellable for this operation
+ *
+ * @return the found media object.
+ */
+ public override async MediaObject? find_object (string id,
+ Cancellable? cancellable)
+ throws Error {
+ return null;
+ }
+
+}
+
+
+
+/**
* CreateObject action implementation.
*/
-internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
+internal class Rygel.ObjectCreator: GLib.Object, Rygel.StateMachine {
private static PatternSpec comment_pattern = new PatternSpec ("*<!--*-->*");
private const string INVALID_CHARS = "/?<>\\:*|\"";
@@ -36,8 +96,8 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
private string container_id;
private string elements;
- private DIDLLiteItem didl_item;
- private MediaItem item;
+ private DIDLLiteObject didl_object;
+ private MediaObject object;
private ContentDirectory content_dir;
private ServiceAction action;
@@ -47,8 +107,8 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
public Cancellable cancellable { get; set; }
- public ItemCreator (ContentDirectory content_dir,
- owned ServiceAction action) {
+ public ObjectCreator (ContentDirectory content_dir,
+ owned ServiceAction action) {
this.content_dir = content_dir;
this.cancellable = content_dir.cancellable;
this.action = (owned) action;
@@ -76,30 +136,38 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
* modify the UPnP class to something we support and
* fetch_container took care of this already.
*/
- if (!container.can_create (this.didl_item.upnp_class) &&
+ if (!container.can_create (this.didl_object.upnp_class) &&
this.container_id != MediaContainer.ANY) {
throw new ContentDirectoryError.BAD_METADATA
("Creating of objects with class %s " +
"is not supported in %s",
- this.didl_item.upnp_class,
+ this.didl_object.upnp_class,
container.id);
}
- yield this.create_item_from_didl (container);
- yield container.add_item (this.item, this.cancellable);
+ yield this.create_object_from_didl (container);
+ if (this.object is MediaItem) {
+ yield container.add_item (this.object as MediaItem,
+ this.cancellable);
+ } else {
+ yield container.add_container (this.object as MediaContainer,
+ this.cancellable);
+ }
- yield this.wait_for_item (container);
+ yield this.wait_for_object (container);
- this.item.serialize (serializer, this.content_dir.http_server);
+ this.object.serialize (serializer, this.content_dir.http_server);
// Conclude the successful action
this.conclude ();
if (this.container_id == MediaContainer.ANY &&
- this.item.place_holder) {
- var queue = ItemRemovalQueue.get_default ();
+ ((this.object is MediaContainer) ||
+ (this.object is MediaItem && (this.object as
+ MediaItem).place_holder))) {
+ var queue = ObjectRemovalQueue.get_default ();
- queue.queue (this.item, this.cancellable);
+ queue.queue (this.object, this.cancellable);
}
} catch (Error err) {
this.handle_error (err);
@@ -136,8 +204,10 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
* according to UPnP and DLNA guidelines.
*/
private void parse_didl () throws Error {
- this.didl_parser.item_available.connect ((didl_item) => {
- this.didl_item = didl_item;
+ // FIXME: This will take the last object in the DIDL-Lite, maybe we
+ // should limit it to one somehow.
+ this.didl_parser.object_available.connect ((didl_object) => {
+ this.didl_object = didl_object;
});
try {
@@ -146,27 +216,28 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
throw new ContentDirectoryError.BAD_METADATA ("Bad metadata");
}
- if (this.didl_item == null) {
+ if (this.didl_object == null) {
+ // FIXME: Change to object after string freeze
var message = _("No items in DIDL-Lite from client: '%s'");
throw new ContentDirectoryError.BAD_METADATA
(message, this.elements);
}
- if (didl_item.id == null || didl_item.id != "") {
+ if (didl_object.id == null || didl_object.id != "") {
throw new ContentDirectoryError.BAD_METADATA
("@id must be set to \"\" in " +
"CreateItem");
}
- if (didl_item.title == null) {
+ if (didl_object.title == null) {
throw new ContentDirectoryError.BAD_METADATA
("dc:title must be set in " +
"CreateItem");
}
// FIXME: Is this check really necessary? 7.3.118.4 passes without it.
- if ((didl_item.dlna_managed &
+ if ((didl_object.dlna_managed &
(OCMFlags.UPLOAD |
OCMFlags.CREATE_CONTAINER |
OCMFlags.UPLOAD_DESTROYABLE)) != 0) {
@@ -175,14 +246,14 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
"were found in 'dlnaManaged'");
}
- if (didl_item.upnp_class == null ||
- didl_item.upnp_class == "" ||
- !didl_item.upnp_class.has_prefix ("object.item")) {
+ if (didl_object.upnp_class == null ||
+ didl_object.upnp_class == "" ||
+ !didl_object.upnp_class.has_prefix ("object")) {
throw new ContentDirectoryError.BAD_METADATA
("Invalid upnp:class given ");
}
- if (didl_item.restricted) {
+ if (didl_object.restricted) {
throw new ContentDirectoryError.INVALID_ARGS
("Cannot create restricted item");
}
@@ -222,13 +293,13 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
return null;
}
- var upnp_class = this.didl_item.upnp_class;
+ var upnp_class = this.didl_object.upnp_class;
var expression = new RelationalExpression ();
expression.op = SearchCriteriaOp.DERIVED_FROM;
expression.operand1 = "upnp:createClass";
- while (upnp_class != "object.item") {
+ while (upnp_class != "object") {
expression.operand2 = upnp_class;
uint total_matches;
@@ -239,7 +310,7 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
root_container.sort_criteria,
this.cancellable);
if (result.size > 0) {
- this.didl_item.upnp_class = upnp_class;
+ this.didl_object.upnp_class = upnp_class;
return result[0];
} else {
@@ -247,10 +318,11 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
}
}
- if (upnp_class == "object.item") {
+ if (upnp_class == "object") {
+ // FIXME: Mark translatable.
throw new ContentDirectoryError.BAD_METADATA
("'%s' UPnP class unsupported",
- this.didl_item.upnp_class);
+ this.didl_object.upnp_class);
}
return null;
@@ -296,7 +368,7 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
string didl = this.serializer.get_string ();
/* Set action return arguments */
- this.action.set ("ObjectID", typeof (string), this.item.id,
+ this.action.set ("ObjectID", typeof (string), this.object.id,
"Result", typeof (string), didl);
this.action.return ();
@@ -318,9 +390,15 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
}
private string get_generic_mime_type () {
- if (this.item is ImageItem) {
+ if (!(this.object is MediaItem)) {
+ return "";
+ }
+
+ var item = this.object as MediaItem;
+
+ if (item is ImageItem) {
return "image";
- } else if (this.item is VideoItem) {
+ } else if (item is VideoItem) {
return "video";
} else {
return "audio";
@@ -328,23 +406,49 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
}
/**
- * Transfer information passed by caller to a MediaItem.
+ * Transfer information passed by caller to a MediaObject.
*
- * WritableContainer works on MediaItem so we transfer the supplied data to
- * one. Additionally some checks are performed (e.g. whether the DLNA
+ * WritableContainer works on MediaObject so we transfer the supplied data
+ * to one. Additionally some checks are performed (e.g. whether the DLNA
* profile is supported or not) or sanitize the supplied title for use as
* part of the on-disk filename.
*
- * This function fills ItemCreator.item.
+ * This function fills ObjectCreator.object.
*/
- private async void create_item_from_didl (WritableContainer container)
- throws Error {
- this.item = this.create_item (this.didl_item.id,
- container,
- this.didl_item.title,
- this.didl_item.upnp_class);
-
- var resources = this.didl_item.get_resources ();
+ private async void create_object_from_didl (WritableContainer container)
+ throws Error {
+ this.object = this.create_object (this.didl_object.id,
+ container,
+ this.didl_object.title,
+ this.didl_object.upnp_class);
+
+ if (this.object is MediaItem) {
+ this.extract_item_parameters ();
+ }
+
+ // extract_item_parameters could not find an uri
+ if (this.object.uris.size == 0) {
+ var uri = yield this.create_uri (container, this.object.title);
+ this.object.uris.add (uri);
+ if (this.object is MediaItem) {
+ (this.object as MediaItem).place_holder = true;
+ }
+ } else {
+ if (this.object is MediaItem) {
+ var file = File.new_for_uri (this.object.uris[0]);
+ (this.object as MediaItem).place_holder = !file.is_native ();
+ }
+ }
+
+ this.object.id = this.object.uris[0];
+
+ this.parse_and_verify_didl_date ();
+ }
+
+ private void extract_item_parameters () throws Error {
+ var item = this.object as MediaItem;
+
+ var resources = this.didl_object.get_resources ();
if (resources != null && resources.length () > 0) {
var resource = resources.nth (0).data;
var info = resource.protocol_info;
@@ -352,90 +456,86 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
if (info != null) {
if (info.dlna_profile != null) {
if (!this.is_profile_valid (info.dlna_profile)) {
+ // FIXME: Missing translation
throw new ContentDirectoryError.BAD_METADATA
("'%s' DLNA profile unsupported",
info.dlna_profile);
}
- this.item.dlna_profile = info.dlna_profile;
+ item.dlna_profile = info.dlna_profile;
}
if (info.mime_type != null) {
- this.item.mime_type = info.mime_type;
+ item.mime_type = info.mime_type;
}
}
- string sanitized_uri;
+ string sanitized_uri = null;
if (this.is_valid_uri (resource.uri, out sanitized_uri)) {
- this.item.add_uri (sanitized_uri);
+ item.add_uri (sanitized_uri);
}
if (resource.size >= 0) {
- this.item.size = resource.size;
+ item.size = resource.size;
}
}
- if (this.item.mime_type == null) {
- this.item.mime_type = this.get_generic_mime_type ();
- }
-
- if (this.item.size < 0) {
- this.item.size = 0;
+ if (item.mime_type == null) {
+ item.mime_type = this.get_generic_mime_type ();
}
- if (this.item.uris.size == 0) {
- var uri = yield this.create_uri (container, this.item.title);
- this.item.uris.add (uri);
- this.item.place_holder = true;
- } else {
- var file = File.new_for_uri (this.item.uris[0]);
- this.item.place_holder = !file.is_native ();
+ if (item.size < 0) {
+ item.size = 0;
}
-
- this.item.id = this.item.uris[0];
-
- this.parse_and_verify_didl_date ();
}
private void parse_and_verify_didl_date () throws Error {
- if (this.didl_item.date == null) {
+ if (!(this.didl_object is DIDLLiteItem)) {
return;
}
- var parsed_date = new Soup.Date.from_string (this.didl_item.date);
+ var didl_item = this.didl_object as DIDLLiteItem;
+ if (didl_item.date == null) {
+ return;
+ }
+
+ var parsed_date = new Soup.Date.from_string (didl_item.date);
if (parsed_date != null) {
- this.item.date = parsed_date.to_string (Soup.DateFormat.ISO8601);
+ (this.object as MediaItem).date = parsed_date.to_string
+ (Soup.DateFormat.ISO8601);
return;
}
int year = 0, month = 0, day = 0;
- if (this.didl_item.date.scanf ("%4d-%02d-%02d",
- out year,
- out month,
- out day) != 3) {
+ if (didl_item.date.scanf ("%4d-%02d-%02d",
+ out year,
+ out month,
+ out day) != 3) {
throw new ContentDirectoryError.BAD_METADATA
("Invalid date format: %s",
- this.didl_item.date);
+ didl_item.date);
}
var date = GLib.Date ();
date.set_dmy ((DateDay) day, (DateMonth) month, (DateYear) year);
if (!date.valid ()) {
+ // FIXME: Add translation.
throw new ContentDirectoryError.BAD_METADATA
("Invalid date: %s",
- this.didl_item.date);
+ didl_item.date);
}
- this.item.date = this.didl_item.date + "T00:00:00";
+ (this.object as MediaItem).date = didl_item.date + "T00:00:00";
}
- private MediaItem create_item (string id,
- WritableContainer parent,
- string title,
- string upnp_class) throws Error {
+ private MediaObject create_object (string id,
+ WritableContainer parent,
+ string title,
+ string upnp_class)
+ throws Error {
switch (upnp_class) {
case ImageItem.UPNP_CLASS:
return new ImageItem (id, parent, title);
@@ -449,6 +549,12 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
return new MusicItem (id, parent, title);
case PlaylistItem.UPNP_CLASS:
return new PlaylistItem (id, parent, title);
+ case MediaContainer.STORAGE_FOLDER:
+ return new BaseMediaContainer (id, parent, title, 0);
+ case MediaContainer.PLAYLIST:
+ var container = new BaseMediaContainer (id, parent, title, 0);
+ container.upnp_class = upnp_class;
+ return container;
default:
throw new ContentDirectoryError.BAD_METADATA
("Creation of item of class '%s' " +
@@ -501,13 +607,7 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
"_",
RegexMatchFlags.NOTEMPTY);
- var udn = new uchar[50];
- var id = new uchar[16];
-
- UUID.generate (id);
- UUID.unparse (id, udn);
-
- return (string) udn + "-" + mangled;
+ return UUID.get () + "-" + mangled;
}
/**
@@ -535,37 +635,36 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
}
/**
- * Wait for the new item
+ * Wait for the new object
*
- * When creating an item in the back-end via WritableContainer.add_item
- * there might be a delay between the creation and the back-end having the
- * newly created item available. This function waits for the item to become
- * available by hooking into the container_updated signal. The maximum time
- * to wait is 5 seconds.
+ * When creating an object in the back-end via WritableContainer.add_item
+ * or WritableContainer.add_container there might be a delay between the
+ * creation and the back-end having the newly created item available. This
+ * function waits for the item to become available by hooking into the
+ * container_updated signal. The maximum time to wait is 5 seconds.
*
* @param container to watch
*/
- private async void wait_for_item (WritableContainer container) {
- debug ("Waiting for new item to appear under container '%s'..",
+ private async void wait_for_object (WritableContainer container) {
+ debug ("Waiting for new object to appear under container '%s'..",
container.id);
- MediaItem item = null;
+ MediaObject object = null;
- while (item == null) {
+ while (object == null) {
try {
- item = (yield container.find_object (this.item.id,
- this.cancellable))
- as MediaItem;
+ object = yield container.find_object (this.object.id,
+ this.cancellable);
} catch (Error error) {
warning ("Error from container '%s' on trying to find newly " +
- "added child item '%s' in it",
+ "added child object '%s' in it",
container.id,
- this.item.id);
+ this.object.id);
}
- if (item == null) {
+ if (object == null) {
var id = container.container_updated.connect ((container) => {
- this.wait_for_item.callback ();
+ this.wait_for_object.callback ();
});
uint timeout = 0;
@@ -573,7 +672,7 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
debug ("Timeout on waiting for 'updated' signal on '%s'.",
container.id);
timeout = 0;
- this.wait_for_item.callback ();
+ this.wait_for_object.callback ();
return false;
});
@@ -589,7 +688,7 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
}
}
}
- debug ("Finished waiting for new item to appear under container '%s'",
+ debug ("Finished waiting for new object to appear under container '%s'",
container.id);
}
diff --git a/src/librygel-server/rygel-object-removal-queue.vala
b/src/librygel-server/rygel-object-removal-queue.vala
new file mode 100644
index 0000000..b43da44
--- /dev/null
+++ b/src/librygel-server/rygel-object-removal-queue.vala
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 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 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+using Gee;
+
+/**
+ * Queues objects for removal after 35 seconds or immediately.
+ *
+ * The 35s timeout comes from the DLNA documentation.
+ */
+internal class Rygel.ObjectRemovalQueue: GLib.Object {
+ private const uint TIMEOUT = 35;
+
+ private static ObjectRemovalQueue removal_queue;
+
+ private HashMap<string,uint> object_timeouts;
+
+ public static ObjectRemovalQueue get_default () {
+ if (unlikely (removal_queue == null)) {
+ removal_queue = new ObjectRemovalQueue ();
+ }
+
+ return removal_queue;
+ }
+
+ public void queue (MediaObject object, Cancellable? cancellable) {
+ if (object.parent_ref == null) {
+ object.parent_ref = object.parent;
+ }
+
+ var timeout = Timeout.add_seconds (TIMEOUT, () => {
+ debug ("Timeout on temporary object '%s'.", object.id);
+ this.remove_now.begin (object, cancellable);
+
+ return false;
+ });
+
+ object_timeouts.set (object.id, timeout);
+ }
+
+ public bool dequeue (MediaObject object) {
+ uint timeout;
+
+ if (object_timeouts.unset (object.id, out timeout)) {
+ Source.remove (timeout);
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public async void remove_now (MediaObject object, Cancellable? cancellable) {
+ object_timeouts.unset (object.id);
+
+ var parent = object.parent as WritableContainer;
+
+ try {
+ if (object is MediaItem) {
+ yield parent.remove_item (object.id, cancellable);
+ } else {
+ yield parent.remove_container (object.id, cancellable);
+ }
+
+ debug ("Auto-destroyed object '%s'!", object.id);
+ } catch (Error err) {
+ warning ("Failed to auto-destroy temporary object '%s': %s",
+ object.id,
+ err.message);
+ }
+ }
+
+ private ObjectRemovalQueue () {
+ object_timeouts = new HashMap<string,uint> ();
+ }
+}
diff --git a/src/librygel-server/rygel-writable-container.vala
b/src/librygel-server/rygel-writable-container.vala
index 48d6056..b6a384d 100644
--- a/src/librygel-server/rygel-writable-container.vala
+++ b/src/librygel-server/rygel-writable-container.vala
@@ -26,6 +26,10 @@
using Gee;
+public errordomain WriteableContainerError {
+ NOT_IMPLEMENTED
+}
+
/**
* This interface should be implemented by 'writable' containers - ones that allow
* adding (via upload), removal and editing of items directly under them.
@@ -72,7 +76,6 @@ public interface Rygel.WritableContainer : MediaContainer {
* is handled by the container class.
*
* This method corresponds to the UPnP ContentDirectory's CreateObject action.
- * Currently there is no way to add child containers.
*
* @param item The item to add to this container
* @param cancellable optional cancellable for this operation
@@ -82,6 +85,16 @@ public interface Rygel.WritableContainer : MediaContainer {
public async abstract void add_item (MediaItem item,
Cancellable? cancellable) throws Error;
+
+ /**
+ * Add a new container directly under this container.
+ *
+ * @param container The container to add to this container
+ * @param cancellable optional cancellable for this operation
+ **/
+ public async abstract void add_container (MediaContainer container,
+ Cancellable? cancellable)
+ throws Error;
/**
* Remove an item directly under this container that has the ID @id.
*
@@ -97,4 +110,19 @@ public interface Rygel.WritableContainer : MediaContainer {
*/
public async abstract void remove_item (string id, Cancellable? cancellable)
throws Error;
+
+ /**
+ * Remove a container directly under this container that has the ID @id.
+ *
+ * The caller should not first remove the file(s) pointed to by the item's URI(s). That
+ * is handled by the container class.
+ *
+ * This method corresponds to the UPnP ContentDirectory's DestroyObject action.
+ *
+ * @param id The ID of the item to remove from this container
+ * @param cancellable optional cancellable for this operation
+ */
+ public async abstract void remove_container (string id,
+ Cancellable? cancellable)
+ throws Error;
}
diff --git a/src/plugins/media-export/rygel-media-export-writable-db-container.vala
b/src/plugins/media-export/rygel-media-export-writable-db-container.vala
index d8940b9..be1a0a6 100644
--- a/src/plugins/media-export/rygel-media-export-writable-db-container.vala
+++ b/src/plugins/media-export/rygel-media-export-writable-db-container.vala
@@ -43,12 +43,17 @@ internal class Rygel.MediaExport.WritableDbContainer : TrackableDbContainer,
base.constructed ();
this.create_classes = new ArrayList<string> ();
+
+ // Items
this.create_classes.add (Rygel.ImageItem.UPNP_CLASS);
this.create_classes.add (Rygel.PhotoItem.UPNP_CLASS);
this.create_classes.add (Rygel.VideoItem.UPNP_CLASS);
this.create_classes.add (Rygel.AudioItem.UPNP_CLASS);
this.create_classes.add (Rygel.MusicItem.UPNP_CLASS);
this.create_classes.add (Rygel.PlaylistItem.UPNP_CLASS);
+
+ // Containers
+ this.create_classes.add (Rygel.MediaContainer.STORAGE_FOLDER);
}
public async void add_item (Rygel.MediaItem item, Cancellable? cancellable)
@@ -63,10 +68,36 @@ internal class Rygel.MediaExport.WritableDbContainer : TrackableDbContainer,
yield this.add_child_tracked (item);
}
+ public async void add_container (MediaContainer container,
+ Cancellable? cancellable)
+ throws Error {
+ container.parent = this;
+ switch (container.upnp_class) {
+ case MediaContainer.STORAGE_FOLDER:
+ var file = File.new_for_uri (container.uris[0]);
+ if (file.is_native ()) {
+ file.make_directory_with_parents (cancellable);
+ }
+ break;
+ default:
+ throw new WriteableContainerError.NOT_IMPLEMENTED
+ ("upnp:class %s not supported",
+ container.upnp_class);
+ }
+
+ yield this.add_child_tracked (container);
+ }
+
public async void remove_item (string id, Cancellable? cancellable)
throws Error {
var object = this.media_db.get_object (id);
yield this.remove_child_tracked (object);
}
+
+ public async void remove_container (string id, Cancellable? cancellable)
+ throws Error {
+ throw new WriteableContainerError.NOT_IMPLEMENTED ("Not supported");
+ }
+
}
diff --git a/src/plugins/tracker/rygel-tracker-category-all-container.vala
b/src/plugins/tracker/rygel-tracker-category-all-container.vala
index dd52e92..83c104d 100644
--- a/src/plugins/tracker/rygel-tracker-category-all-container.vala
+++ b/src/plugins/tracker/rygel-tracker-category-all-container.vala
@@ -90,6 +90,11 @@ public class Rygel.Tracker.CategoryAllContainer : SearchContainer,
item.parent = this;
}
+ public async void add_container (MediaContainer container,
+ Cancellable? cancellable) throws Error {
+ throw new WriteableContainerError.NOT_IMPLEMENTED ("Not supported");
+ }
+
public async void remove_item (string id, Cancellable? cancellable)
throws Error {
string parent_id;
@@ -99,6 +104,11 @@ public class Rygel.Tracker.CategoryAllContainer : SearchContainer,
yield this.remove_entry_from_store (urn);
}
+ public async void remove_container (string id, Cancellable? cancellable)
+ throws Error {
+ throw new WriteableContainerError.NOT_IMPLEMENTED ("Not supported");
+ }
+
public async MediaObjects? search (SearchExpression? expression,
uint offset,
uint max_count,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6f4fc54..dd56495 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -8,7 +8,7 @@ check_PROGRAMS = rygel-http-item-uri-test \
rygel-album-art-spec-test \
rygel-http-post-test \
rygel-searchable-container-test \
- rygel-item-creator-test \
+ rygel-object-creator-test \
rygel-user-config-test \
rygel-regression \
rygel-media-engine-test
@@ -135,22 +135,22 @@ rygel_searchable_container_test_CFLAGS = \
rygel_searchable_container_test_LDADD = \
$(test_libs)
-rygel_item_creator_test_SOURCES = rygel-item-creator-test.vala \
- rygel-item-creator.vala \
+rygel_object_creator_test_SOURCES = rygel-object-creator-test.vala \
+ rygel-object-creator.vala \
rygel-data-source.vala \
rygel-dlna-profile.vala \
- rygel-http-seek_item-creator.vala \
- rygel-state-machine_item-creator.vala \
+ rygel-http-seek_object-creator.vala \
+ rygel-state-machine_object-creator.vala \
rygel-relational-expression.vala \
rygel-search-expression.vala \
rygel-media-engine.vala \
- rygel-serializer_item-creator.vala
-rygel_item_creator_test_VALAFLAGS = \
+ rygel-serializer_object-creator.vala
+rygel_object_creator_test_VALAFLAGS = \
$(test_valaflags) \
--pkg uuid
-rygel_item_creator_test_CFLAGS = \
+rygel_object_creator_test_CFLAGS = \
$(test_cflags)
-rygel_item_creator_test_LDADD = \
+rygel_object_creator_test_LDADD = \
$(test_libs)
rygel_user_config_test_SOURCES = rygel-configuration.vala \
diff --git a/tests/rygel-http-seek_item-creator.vala b/tests/rygel-http-seek_object-creator.vala
similarity index 100%
rename from tests/rygel-http-seek_item-creator.vala
rename to tests/rygel-http-seek_object-creator.vala
diff --git a/tests/rygel-item-creator-test.vala b/tests/rygel-object-creator-test.vala
similarity index 88%
rename from tests/rygel-item-creator-test.vala
rename to tests/rygel-object-creator-test.vala
index 019d137..f2cbb45 100644
--- a/tests/rygel-item-creator-test.vala
+++ b/tests/rygel-object-creator-test.vala
@@ -99,21 +99,21 @@ public class Rygel.ServiceAction : GLib.Object {
public class Rygel.HTTPServer : GLib.Object {
}
-public class Rygel.ItemRemovalQueue : GLib.Object {
- public static ItemRemovalQueue get_default () {
- return new ItemRemovalQueue ();
+public class Rygel.ObjectRemovalQueue : GLib.Object {
+ public static ObjectRemovalQueue get_default () {
+ return new ObjectRemovalQueue ();
}
- public void queue (MediaItem item, Cancellable? cancellable) {
+ public void queue (MediaObject object, Cancellable? cancellable) {
}
}
public class Rygel.MediaObject : GLib.Object {
- public string id;
+ public string id {get; set; }
public string ref_id;
- public unowned MediaContainer parent;
+ public unowned MediaContainer parent { get; set; }
public string upnp_class;
- public string title;
+ public string title { get; set; }
public GUPnP.OCMFlags ocm_flags;
public Gee.ArrayList<string> uris;
public uint object_update_id;
@@ -121,6 +121,24 @@ public class Rygel.MediaObject : GLib.Object {
public void add_uri (string uri) {
this.uris.add (uri);
}
+
+ internal void serialize (Rygel.Serializer serializer, HTTPServer server) {
+ }
+
+ public virtual async MediaObjects? get_children
+ (uint offset,
+ uint max_count,
+ string sort_criteria,
+ Cancellable? cancellable)
+ throws Error {
+ return null;
+ }
+
+ public virtual async MediaObject? find_object (string id,
+ Cancellable? cancellable)
+ throws Error {
+ return null;
+ }
}
public interface Rygel.TrackableContainer : Rygel.MediaContainer {
@@ -142,8 +160,6 @@ public class Rygel.MediaItem : Rygel.MediaObject {
this.title = title;
}
- internal void serialize (Rygel.Serializer serializer, HTTPServer server) {
- }
}
public class Rygel.MusicItem : Rygel.AudioItem {
@@ -202,15 +218,19 @@ public class Rygel.ContentDirectory : GLib.Object {
public class Rygel.MediaContainer : Rygel.MediaObject {
public Gee.ArrayList<string> create_classes = new Gee.ArrayList<string> ();
- public int child_count;
+ public int child_count { get; set; }
public string sort_criteria = "+dc:title";
public static const string ANY = "DLNA.ORG_AnyContainer";
+ public static const string STORAGE_FOLDER =
+ "object.container.storageFolder";
+ public static const string PLAYLIST =
+ "object.container.playlistContainer";
public uint update_id;
// mockable elements
public MediaObject found_object = null;
- public async MediaObject? find_object (string id,
+ public override async MediaObject? find_object (string id,
Cancellable? cancellable = null)
throws Error {
Idle.add (() => { find_object.callback (); return false; });
@@ -237,6 +257,9 @@ public class Rygel.WritableContainer : Rygel.MediaContainer {
public async void add_item (MediaItem item,
Cancellable? cancellable = null) {
}
+
+ public async void add_container (MediaContainer container, Cancellable?
+ cancellable = null) { }
}
public class Rygel.SearchableContainer : Rygel.MediaContainer {
@@ -310,11 +333,11 @@ public static void log_func (string? domain,
}
}
-public class Rygel.HTTPItemCreatorTest : GLib.Object {
+public class Rygel.HTTPObjectCreatorTest : GLib.Object {
public static int main (string[] args) {
Log.set_default_handler (log_func);
- var test = new HTTPItemCreatorTest ();
+ var test = new HTTPObjectCreatorTest ();
test.test_parse_args ();
test.test_didl_parsing ();
test.test_fetch_container ();
@@ -334,7 +357,7 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
Error bad_metadata;
Error invalid_args;
- public HTTPItemCreatorTest () {
+ public HTTPObjectCreatorTest () {
this.no_such_object = new ContentDirectoryError.NO_SUCH_OBJECT("");
this.restricted_parent = new ContentDirectoryError.RESTRICTED_PARENT("");
this.bad_metadata = new ContentDirectoryError.BAD_METADATA("");
@@ -346,19 +369,19 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
var content_directory = new ContentDirectory ();
var action = new ServiceAction (null, "");
- var creator = new ItemCreator (content_directory, action);
+ var creator = new ObjectCreator (content_directory, action);
creator.run.begin ();
assert (action.error_code == no_such_object.code);
// check elements containing a comment
action = new ServiceAction ("0", "<!-- This is an XML comment -->");
- creator = new ItemCreator (content_directory, action);
+ creator = new ObjectCreator (content_directory, action);
creator.run.begin ();
assert (action.error_code == bad_metadata.code);
// check null elements
action = new ServiceAction ("0", null);
- creator = new ItemCreator (content_directory, action);
+ creator = new ObjectCreator (content_directory, action);
creator.run.begin ();
assert (action.error_code == bad_metadata.code);
}
@@ -369,7 +392,7 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
doc->dump_memory_enc (out xml);
var action = new ServiceAction ("0", xml);
var content_directory = new ContentDirectory ();
- var creator = new ItemCreator (content_directory, action);
+ var creator = new ObjectCreator (content_directory, action);
creator.run.begin ();
assert (action.error_code == expected_code);
}
@@ -387,7 +410,7 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
// test no DIDL
var action = new ServiceAction ("0", "");
- var creator = new ItemCreator (content_directory, action);
+ var creator = new ObjectCreator (content_directory, action);
creator.run.begin ();
assert (action.error_code == bad_metadata.code);
assert (action.error_message == "Bad metadata");
@@ -426,13 +449,10 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
didl_node->add_child (tmp);
this.test_didl_parsing_step (xml, bad_metadata.code);
- // test missing, empty or non-item upnp class
+ // test missing or empty upnp class
tmp->unlink ();
tmp = item_node->copy (1);
var class_node = tmp->children->next;
- class_node->set_content ("object.container");
- didl_node->add_child (tmp);
- this.test_didl_parsing_step (xml, bad_metadata.code);
class_node->set_content ("");
this.test_didl_parsing_step (xml, bad_metadata.code);
@@ -441,7 +461,7 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
this.test_didl_parsing_step (xml, bad_metadata.code);
}
- private void test_fetch_container_run (ItemCreator creator) {
+ private void test_fetch_container_run (ObjectCreator creator) {
var main_loop = new MainLoop (null, false);
creator.run.begin ( () => { main_loop.quit (); });
main_loop.run ();
@@ -453,7 +473,7 @@ public class Rygel.HTTPItemCreatorTest : GLib.Object {
var root_container = new SearchableContainer ();
content_directory.root_container = root_container;
var action = new ServiceAction ("0", DIDL_ITEM);
- var creator = new ItemCreator (content_directory, action);
+ var creator = new ObjectCreator (content_directory, action);
this.test_fetch_container_run (creator);
assert (action.error_code == no_such_object.code);
diff --git a/tests/rygel-object-creator.vala b/tests/rygel-object-creator.vala
new file mode 120000
index 0000000..d790f61
--- /dev/null
+++ b/tests/rygel-object-creator.vala
@@ -0,0 +1 @@
+../src/librygel-server/rygel-object-creator.vala
\ No newline at end of file
diff --git a/tests/rygel-serializer_item-creator.vala b/tests/rygel-serializer_object-creator.vala
similarity index 100%
rename from tests/rygel-serializer_item-creator.vala
rename to tests/rygel-serializer_object-creator.vala
diff --git a/tests/rygel-state-machine_item-creator.vala b/tests/rygel-state-machine_object-creator.vala
similarity index 100%
rename from tests/rygel-state-machine_item-creator.vala
rename to tests/rygel-state-machine_object-creator.vala
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]