diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | data/xml/ContentDirectory-NoTrack.xml.in | 32 | ||||
-rw-r--r-- | data/xml/ContentDirectory.xml.in | 32 | ||||
-rw-r--r-- | src/librygel-server/filelist.am | 1 | ||||
-rw-r--r-- | src/librygel-server/rygel-content-directory.vala | 15 | ||||
-rw-r--r-- | src/librygel-server/rygel-item-updater.vala | 178 | ||||
-rw-r--r-- | src/librygel-server/rygel-media-item.vala | 7 | ||||
-rw-r--r-- | src/librygel-server/rygel-media-object.vala | 27 | ||||
-rw-r--r-- | src/librygel-server/rygel-music-item.vala | 19 | ||||
-rw-r--r-- | src/librygel-server/rygel-photo-item.vala | 14 | ||||
-rw-r--r-- | src/librygel-server/rygel-video-item.vala | 14 |
11 files changed, 340 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index 183756a2..0457848a 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ VALA_REQUIRED=0.16.1 VALADOC_REQUIRED=0.2 GSSDP_REQUIRED=0.13.0 GUPNP_REQUIRED=0.19.0 -GUPNP_AV_REQUIRED=0.11.0 +GUPNP_AV_REQUIRED=0.11.2 GUPNP_DLNA_REQUIRED=0.5.0 GSTREAMER_REQUIRED=0.10.36 GSTPBU_REQUIRED=0.10.35 diff --git a/data/xml/ContentDirectory-NoTrack.xml.in b/data/xml/ContentDirectory-NoTrack.xml.in index b40ceefc..4c336fae 100644 --- a/data/xml/ContentDirectory-NoTrack.xml.in +++ b/data/xml/ContentDirectory-NoTrack.xml.in @@ -90,6 +90,11 @@ </stateVariable> <stateVariable sendEvents="no"> + <name>A_ARG_TYPE_TagValueList</name> + <dataType>string</dataType> + </stateVariable> + + <stateVariable sendEvents="no"> <name>A_ARG_TYPE_TransferID</name> <dataType>ui4</dataType> </stateVariable> @@ -330,6 +335,33 @@ </action> <action> + <name>UpdateObject</name> + <argumentList> + <argument> + <name>ObjectID</name> + <direction>in</direction> + <relatedStateVariable> + A_ARG_TYPE_ObjectID + </relatedStateVariable> + </argument> + <argument> + <name>CurrentTagValue</name> + <direction>in</direction> + <relatedStateVariable> + A_ARG_TYPE_TagValueList + </relatedStateVariable> + </argument> + <argument> + <name>NewTagValue</name> + <direction>in</direction> + <relatedStateVariable> + A_ARG_TYPE_TagValueList + </relatedStateVariable> + </argument> + </argumentList> + </action> + + <action> <name>ImportResource</name> <argumentList> <argument> diff --git a/data/xml/ContentDirectory.xml.in b/data/xml/ContentDirectory.xml.in index 2e156c4f..81b82c68 100644 --- a/data/xml/ContentDirectory.xml.in +++ b/data/xml/ContentDirectory.xml.in @@ -95,6 +95,11 @@ </stateVariable> <stateVariable sendEvents="no"> + <name>A_ARG_TYPE_TagValueList</name> + <dataType>string</dataType> + </stateVariable> + + <stateVariable sendEvents="no"> <name>A_ARG_TYPE_TransferID</name> <dataType>ui4</dataType> </stateVariable> @@ -335,6 +340,33 @@ </action> <action> + <name>UpdateObject</name> + <argumentList> + <argument> + <name>ObjectID</name> + <direction>in</direction> + <relatedStateVariable> + A_ARG_TYPE_ObjectID + </relatedStateVariable> + </argument> + <argument> + <name>CurrentTagValue</name> + <direction>in</direction> + <relatedStateVariable> + A_ARG_TYPE_TagValueList + </relatedStateVariable> + </argument> + <argument> + <name>NewTagValue</name> + <direction>in</direction> + <relatedStateVariable> + A_ARG_TYPE_TagValueList + </relatedStateVariable> + </argument> + </argumentList> + </action> + + <action> <name>ImportResource</name> <argumentList> <argument> diff --git a/src/librygel-server/filelist.am b/src/librygel-server/filelist.am index d96fe7e5..87794eb1 100644 --- a/src/librygel-server/filelist.am +++ b/src/librygel-server/filelist.am @@ -49,6 +49,7 @@ LIBRYGEL_SERVER_NONVAPI_SOURCE_FILES = \ rygel-import-resource.vala \ rygel-item-creator.vala \ rygel-item-destroyer.vala \ + rygel-item-updater.vala \ rygel-item-removal-queue.vala \ rygel-last-change-entry.vala \ rygel-last-change-obj-add.vala \ diff --git a/src/librygel-server/rygel-content-directory.vala b/src/librygel-server/rygel-content-directory.vala index 9d94d8be..6de630ee 100644 --- a/src/librygel-server/rygel-content-directory.vala +++ b/src/librygel-server/rygel-content-directory.vala @@ -31,12 +31,18 @@ using Gee; */ internal errordomain Rygel.ContentDirectoryError { NO_SUCH_OBJECT = 701, + INVALID_CURRENT_TAG_VALUE = 702, + INVALID_NEW_TAG_VALUE = 703, + REQUIRED_TAG = 704, + READ_ONLY_TAG = 705, + PARAMETER_MISMATCH = 706, INVALID_SORT_CRITERIA = 709, RESTRICTED_OBJECT = 711, BAD_METADATA = 712, RESTRICTED_PARENT = 713, NO_SUCH_DESTINATION_RESOURCE = 718, CANT_PROCESS = 720, + OUTDATED_OBJECT_METADATA = 728, INVALID_ARGS = 402 } @@ -116,6 +122,7 @@ internal class Rygel.ContentDirectory: Service { this.action_invoked["Search"].connect (this.search_cb); this.action_invoked["CreateObject"].connect (this.create_object_cb); this.action_invoked["DestroyObject"].connect (this.destroy_object_cb); + this.action_invoked["UpdateObject"].connect (this.update_object_cb); this.action_invoked["ImportResource"].connect (this.import_resource_cb); this.action_invoked["GetTransferProgress"].connect ( this.get_transfer_progress_cb); @@ -198,6 +205,14 @@ internal class Rygel.ContentDirectory: Service { destroyer.run.begin (); } + /* UpdateObject action implementation */ + private void update_object_cb (Service content_dir, + ServiceAction action) { + var updater = new ItemUpdater (this, action); + + updater.run.begin (); + } + /* ImportResource action implementation */ private void import_resource_cb (Service content_dir, ServiceAction action) { diff --git a/src/librygel-server/rygel-item-updater.vala b/src/librygel-server/rygel-item-updater.vala new file mode 100644 index 00000000..600ba70e --- /dev/null +++ b/src/librygel-server/rygel-item-updater.vala @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2012 Intel Corporation. + * + * Author: Krzesimir Nowak <krnowak@openismus.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 GUPnP; +using Gee; + +/** + * UpdateObject action implementation. + */ +internal class Rygel.ItemUpdater: GLib.Object, Rygel.StateMachine { + private string object_id; + private string current_tag_value; + private string new_tag_value; + + private ContentDirectory content_dir; + private ServiceAction action; + + public Cancellable cancellable { get; set; } + + public ItemUpdater (ContentDirectory content_dir, + owned ServiceAction action) { + this.content_dir = content_dir; + this.cancellable = content_dir.cancellable; + this.action = (owned) action; + } + + public async void run () { + try { + this.action.get ("ObjectID", + typeof (string), + out this.object_id, + "CurrentTagValue", + typeof (string), + out this.current_tag_value, + "NewTagValue", + typeof (string), + out this.new_tag_value); + if (this.object_id == null) { + // Sorry we can't do anything without the ID + throw new ContentDirectoryError.NO_SUCH_OBJECT + (_("No such object")); + } + + yield this.update_object (); + + this.action.return (); + + debug (_("Successfully updated object '%s'"), this.object_id); + } catch (Error error) { + if (error is ContentDirectoryError) { + this.action.return_error (error.code, error.message); + } else { + this.action.return_error (701, error.message); + } + + warning (_("Failed to update object '%s': %s"), + this.object_id, + error.message); + } + + this.completed (); + } + + private static LinkedList<string> csv_split (string? tag_values) { + var list = new LinkedList<string> (); + var escape = false; + var token_start = 0; + var token_length = 0; + var len = (tag_values != null ? tag_values.length : 0); + + for (int iter = 0; iter < len; ++iter) { + var c = tag_values[iter]; + + if (escape) { + escape = false; + } else { + switch (c) { + case '\\': + escape = true; + + break; + case ',': + list.add (tag_values.substring (token_start, token_length)); + token_start += token_length + 1; + token_length = 0; + + break; + } + } + ++token_length; + } + + // Single tag value only + if (len > 0 && list.is_empty) { + list.add (tag_values); + } + + return list; + } + + private async void update_object () throws Error { + var media_object = yield this.fetch_object (); + var current_list = csv_split (this.current_tag_value); + var new_list = csv_split (this.new_tag_value); + + + switch (media_object.apply_fragments (current_list, + new_list, + this.content_dir.http_server)) { + case DIDLLiteFragmentResult.OK: + break; + case DIDLLiteFragmentResult.CURRENT_BAD_XML: + case DIDLLiteFragmentResult.CURRENT_INVALID: + throw new ContentDirectoryError.INVALID_CURRENT_TAG_VALUE + ("Bad current tag value."); + case DIDLLiteFragmentResult.NEW_BAD_XML: + case DIDLLiteFragmentResult.NEW_INVALID: + throw new ContentDirectoryError.INVALID_NEW_TAG_VALUE + ("Bad current tag value."); + case DIDLLiteFragmentResult.REQUIRED_TAG: + throw new ContentDirectoryError.REQUIRED_TAG + ("Tried to delete required tag."); + case DIDLLiteFragmentResult.READONLY_TAG: + throw new ContentDirectoryError.READ_ONLY_TAG + ("Tried to change read-only property."); + case DIDLLiteFragmentResult.MISMATCH: + throw new ContentDirectoryError.PARAMETER_MISMATCH + ("Parameter count mismatch."); + default: + throw new ContentDirectoryError.NO_SUCH_OBJECT ("Unknown error."); + } + } + + private async MediaObject fetch_object () throws Error { + var media_object = yield this.content_dir.root_container.find_object + (this.object_id, + this.cancellable); + + if (media_object == null) { + throw new ContentDirectoryError.NO_SUCH_OBJECT + (_("No such object")); + } else if (!(OCMFlags.CHANGE_METADATA in media_object.ocm_flags)) { + var msg = _("Metadata modification of object %s not allowed"); + + throw new ContentDirectoryError.RESTRICTED_OBJECT (msg, + media_object.id); + } else if (media_object.parent.restricted) { + var msg = _("Metadata modification of object %s being a child " + + "of restricted object %s not allowed"); + + throw new ContentDirectoryError.RESTRICTED_PARENT + (msg, + media_object.id, + media_object.parent.id); + } + + return media_object; + } +} diff --git a/src/librygel-server/rygel-media-item.vala b/src/librygel-server/rygel-media-item.vala index fc02e60f..4e171a7f 100644 --- a/src/librygel-server/rygel-media-item.vala +++ b/src/librygel-server/rygel-media-item.vala @@ -188,6 +188,13 @@ public abstract class Rygel.MediaItem : MediaObject { } } + internal override void apply_didl_lite (DIDLLiteObject didl_object) { + base.apply_didl_lite (didl_object); + + this.date = didl_object.date; + this.description = didl_object.description; + } + internal override DIDLLiteObject serialize (DIDLLiteWriter writer, HTTPServer http_server) throws Error { diff --git a/src/librygel-server/rygel-media-object.vala b/src/librygel-server/rygel-media-object.vala index 2421c56d..3e9e1d77 100644 --- a/src/librygel-server/rygel-media-object.vala +++ b/src/librygel-server/rygel-media-object.vala @@ -158,6 +158,33 @@ public abstract class Rygel.MediaObject : GLib.Object { HTTPServer http_server) throws Error; + internal virtual void apply_didl_lite (DIDLLiteObject didl_object) { + this.title = didl_object.title; + } + + internal DIDLLiteFragmentResult apply_fragments + (LinkedList<string> current_fragments, + LinkedList<string> new_fragments, + HTTPServer http_server) { + var result = DIDLLiteFragmentResult.UNKNOWN_ERROR; + + try { + var writer = new DIDLLiteWriter (null); + var didl_object = this.serialize (writer, http_server); + + result = didl_object.apply_fragments + (current_fragments.to_array (), + new_fragments.to_array ()); + + if (result == DIDLLiteFragmentResult.OK) { + this.apply_didl_lite (didl_object); + } + + } catch (Error e) {} + + return result; + } + internal virtual int compare_by_property (MediaObject media_object, string property) { switch (property) { diff --git a/src/librygel-server/rygel-music-item.vala b/src/librygel-server/rygel-music-item.vala index 07f35624..4594f1c8 100644 --- a/src/librygel-server/rygel-music-item.vala +++ b/src/librygel-server/rygel-music-item.vala @@ -94,6 +94,25 @@ public class Rygel.MusicItem : AudioItem { } } + private string get_first (GLib.List<DIDLLiteContributor>? contributors) { + if (contributors != null) { + return contributors.data.name; + } + + return ""; + } + + internal override void apply_didl_lite (DIDLLiteObject didl_object) { + base.apply_didl_lite (didl_object); + + this.artist = get_first (didl_object.get_artists ()); + this.track_number = didl_object.track_number; + this.album = didl_object.album; + this.genre = didl_object.genre; + // TODO: Not sure about it. + //this.album_art.uri = didl_object.album_art + } + internal override DIDLLiteObject serialize (DIDLLiteWriter writer, HTTPServer http_server) throws Error { diff --git a/src/librygel-server/rygel-photo-item.vala b/src/librygel-server/rygel-photo-item.vala index eab0d98c..4e8b7f56 100644 --- a/src/librygel-server/rygel-photo-item.vala +++ b/src/librygel-server/rygel-photo-item.vala @@ -58,6 +58,20 @@ public class Rygel.PhotoItem : ImageItem { } } + private string get_first (GLib.List<DIDLLiteContributor>? contributors) { + if (contributors != null) { + return contributors.data.name; + } + + return ""; + } + + internal override void apply_didl_lite (DIDLLiteObject didl_object) { + base.apply_didl_lite (didl_object); + + this.creator = get_first (didl_object.get_creators ()); + } + internal override DIDLLiteObject serialize (DIDLLiteWriter writer, HTTPServer http_server) throws Error { diff --git a/src/librygel-server/rygel-video-item.vala b/src/librygel-server/rygel-video-item.vala index ffcc7b86..dbacc4e3 100644 --- a/src/librygel-server/rygel-video-item.vala +++ b/src/librygel-server/rygel-video-item.vala @@ -137,6 +137,20 @@ public class Rygel.VideoItem : AudioItem, VisualItem { } } + private string get_first (GLib.List<DIDLLiteContributor>? contributors) { + if (contributors != null) { + return contributors.data.name; + } + + return ""; + } + + internal override void apply_didl_lite (DIDLLiteObject didl_object) { + base.apply_didl_lite (didl_object); + + this.author = get_first (didl_object.get_authors ()); + } + internal override DIDLLiteObject serialize (DIDLLiteWriter writer, HTTPServer http_server) throws Error { |