[rygel] server: Add UpdateObject action



commit 5cd2d3d31be50ebbaf79a9e2665b3cc2dc5e7750
Author: Krzesimir Nowak <krnowak openismus com>
Date:   Thu Oct 11 16:29:51 2012 +0200

    server: Add UpdateObject action

 configure.ac                                     |    2 +-
 data/xml/ContentDirectory-NoTrack.xml.in         |   32 ++++
 data/xml/ContentDirectory.xml.in                 |   32 ++++
 src/librygel-server/filelist.am                  |    1 +
 src/librygel-server/rygel-content-directory.vala |   15 ++
 src/librygel-server/rygel-item-updater.vala      |  178 ++++++++++++++++++++++
 src/librygel-server/rygel-media-item.vala        |    7 +
 src/librygel-server/rygel-media-object.vala      |   27 ++++
 src/librygel-server/rygel-music-item.vala        |   19 +++
 src/librygel-server/rygel-photo-item.vala        |   14 ++
 src/librygel-server/rygel-video-item.vala        |   14 ++
 11 files changed, 340 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 183756a..0457848 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 b40ceef..4c336fa 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 2e156c4..81b82c6 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 d96fe7e..87794eb 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 9d94d8b..6de630e 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 0000000..600ba70
--- /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 fc02e60..4e171a7 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 2421c56..3e9e1d7 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 07f3562..4594f1c 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 eab0d98..4e8b7f5 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 ffcc7b8..dbacc4e 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 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]