[rygel/wip/create-reference: 2/3] server, media-export: Allow container creation
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel/wip/create-reference: 2/3] server, media-export: Allow container creation
- Date: Wed, 27 Feb 2013 14:05:15 +0000 (UTC)
commit 3824a6019fca74eb4cdcc5974ecaebca763711f5
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 | 2 +-
src/librygel-server/filelist.am | 2 +-
src/librygel-server/rygel-content-directory.vala | 2 +-
src/librygel-server/rygel-item-removal-queue.vala | 6 +-
src/librygel-server/rygel-media-container.vala | 1 +
...item-creator.vala => rygel-object-creator.vala} | 281 +++++++++++++-------
src/librygel-server/rygel-writable-container.vala | 30 ++-
.../rygel-media-export-writable-db-container.vala | 30 ++
.../rygel-tracker-category-all-container.vala | 10 +
tests/rygel-item-creator-test.vala | 25 ++-
11 files changed, 284 insertions(+), 107 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..fff2c6a 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -68,7 +68,7 @@ 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-item-updater.c
diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am
index d5b1c75..734fb23 100644
--- a/src/librygel-server/filelist.am
+++ b/src/librygel-server/filelist.am
@@ -51,7 +51,7 @@ 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 \
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-item-removal-queue.vala
b/src/librygel-server/rygel-item-removal-queue.vala
index 282b1a1..5ba3c4e 100644
--- a/src/librygel-server/rygel-item-removal-queue.vala
+++ b/src/librygel-server/rygel-item-removal-queue.vala
@@ -40,7 +40,7 @@ internal class Rygel.ItemRemovalQueue: GLib.Object {
return removal_queue;
}
- public void queue (MediaItem item, Cancellable? cancellable) {
+ public void queue (MediaObject item, Cancellable? cancellable) {
if (item.parent_ref == null) {
item.parent_ref = item.parent;
}
@@ -55,7 +55,7 @@ internal class Rygel.ItemRemovalQueue: GLib.Object {
item_timeouts.set (item.id, timeout);
}
- public bool dequeue (MediaItem item) {
+ public bool dequeue (MediaObject item) {
uint timeout;
if (item_timeouts.unset (item.id, out timeout)) {
@@ -67,7 +67,7 @@ internal class Rygel.ItemRemovalQueue: GLib.Object {
}
}
- public async void remove_now (MediaItem item, Cancellable? cancellable) {
+ public async void remove_now (MediaObject item, Cancellable? cancellable) {
item_timeouts.unset (item.id);
var parent = item.parent as WritableContainer;
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 67%
rename from src/librygel-server/rygel-item-creator.vala
rename to src/librygel-server/rygel-object-creator.vala
index e33b6e7..d230e96 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 cration.
+ */
+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) {
+ ((this.object is MediaContainer) ||
+ (this.object is MediaItem && (this.object as
+ MediaItem).place_holder))) {
var queue = ItemRemovalQueue.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_item) => {
+ this.didl_object = didl_item;
});
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";
@@ -335,107 +413,126 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
* 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.item.
*/
- 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);
+
+ var didl_item = this.didl_object as DIDLLiteItem;
+
+ var resources = this.didl_object.get_resources ();
if (resources != null && resources.length () > 0) {
var resource = resources.nth (0).data;
var info = resource.protocol_info;
- if (info != null) {
+ if (info != null && didl_item != 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;
+ (this.object as MediaItem).dlna_profile = info.dlna_profile;
}
if (info.mime_type != null) {
- this.item.mime_type = info.mime_type;
+ (this.object as MediaItem).mime_type = info.mime_type;
}
}
- string sanitized_uri;
- if (this.is_valid_uri (resource.uri, out sanitized_uri)) {
- this.item.add_uri (sanitized_uri);
+ string sanitized_uri = null;
+ if ((this.object is MediaItem) &&
+ this.is_valid_uri (resource.uri, out sanitized_uri)) {
+ (this.object as MediaItem).add_uri (sanitized_uri);
}
- if (resource.size >= 0) {
- this.item.size = resource.size;
+ if (resource.size >= 0 && this.object is MediaItem) {
+ (this.object as MediaItem).size = resource.size;
}
}
- if (this.item.mime_type == null) {
- this.item.mime_type = this.get_generic_mime_type ();
- }
+ var item = this.object as MediaItem;
+ if (this.object is MediaItem) {
+ if (item.mime_type == null) {
+ item.mime_type = this.get_generic_mime_type ();
+ }
- if (this.item.size < 0) {
- this.item.size = 0;
+ if (item.size < 0) {
+ item.size = 0;
+ }
}
- 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;
+ 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) {
+ item.place_holder = true;
+ }
} else {
- var file = File.new_for_uri (this.item.uris[0]);
- this.item.place_holder = !file.is_native ();
+ var file = File.new_for_uri (item.uris[0]);
+ if (this.object is MediaItem) {
+ item.place_holder = !file.is_native ();
+ }
}
- this.item.id = this.item.uris[0];
+ this.object.id = this.object.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 +546,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 +604,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,9 +632,9 @@ 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
+ * When creating an object 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
@@ -545,27 +642,27 @@ internal class Rygel.ItemCreator: GLib.Object, Rygel.StateMachine {
*
* @param container to watch
*/
- private async void wait_for_item (WritableContainer container) {
+ private async void wait_for_object (WritableContainer container) {
debug ("Waiting for new item 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))
+ object = (yield container.find_object (this.object.id,
+ this.cancellable))
as MediaItem;
} catch (Error error) {
warning ("Error from container '%s' on trying to find newly " +
"added child item '%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 +670,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;
});
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..82231e8 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,35 @@ 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 ContentDirectoryError.BAD_METADATA
+ ("upnp:class not supported");
+ break;
+
+ 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/rygel-item-creator-test.vala b/tests/rygel-item-creator-test.vala
index 019d137..bbb78c7 100644
--- a/tests/rygel-item-creator-test.vala
+++ b/tests/rygel-item-creator-test.vala
@@ -104,7 +104,7 @@ public class Rygel.ItemRemovalQueue : GLib.Object {
return new ItemRemovalQueue ();
}
- public void queue (MediaItem item, Cancellable? cancellable) {
+ public void queue (MediaObject item, Cancellable? cancellable) {
}
}
@@ -121,6 +121,9 @@ 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 interface Rygel.TrackableContainer : Rygel.MediaContainer {
@@ -142,8 +145,6 @@ public class Rygel.MediaItem : Rygel.MediaObject {
this.title = title;
}
- internal void serialize (Rygel.Serializer serializer, HTTPServer server) {
- }
}
public class Rygel.MusicItem : Rygel.AudioItem {
@@ -205,6 +206,8 @@ public class Rygel.MediaContainer : Rygel.MediaObject {
public int child_count;
public string sort_criteria = "+dc:title";
public static const string ANY = "DLNA.ORG_AnyContainer";
+ public static const string STORAGE_FOLDER =
+ "object.container.storageFolder";
public uint update_id;
// mockable elements
@@ -222,6 +225,14 @@ public class Rygel.MediaContainer : Rygel.MediaObject {
public signal void container_updated (MediaContainer container);
}
+public class Rygel.BaseMediaContainer : Rygel.MediaContainer {
+ public BaseMediaContainer (string id,
+ MediaContainer? parent,
+ string title,
+ int child_count) {
+ }
+}
+
public class Rygel.MediaObjects : Gee.ArrayList<MediaObject> {
}
@@ -237,6 +248,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 {
@@ -426,13 +440,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);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]