[rygel/wip/create-reference: 5/5] media-export: Implement playlistContainer



commit ed1e742ffad0996f0389fd66e946839b8828bbc0
Author: Jens Georg <jensg openismus com>
Date:   Thu Feb 28 15:00:43 2013 +0100

    media-export: Implement playlistContainer

 src/plugins/media-export/Makefile.am               |    2 +
 .../rygel-media-export-media-cache-upgrader.vala   |   22 ++++
 .../rygel-media-export-media-cache.vala            |    8 +-
 .../rygel-media-export-object-factory.vala         |    8 ++
 .../rygel-media-export-playlist-container.vala     |  109 ++++++++++++++++++++
 ...rygel-media-export-playlist-root-container.vala |   92 +++++++++++++++++
 .../rygel-media-export-sql-factory.vala            |   26 ++++--
 .../rygel-media-export-writable-db-container.vala  |   24 +++--
 8 files changed, 273 insertions(+), 18 deletions(-)
---
diff --git a/src/plugins/media-export/Makefile.am b/src/plugins/media-export/Makefile.am
index 5921883..7c7b11a 100644
--- a/src/plugins/media-export/Makefile.am
+++ b/src/plugins/media-export/Makefile.am
@@ -28,6 +28,8 @@ librygel_media_export_la_SOURCES = \
        rygel-media-export-jpeg-writer.vala \
        rygel-media-export-object-factory.vala \
        rygel-media-export-writable-db-container.vala \
+       rygel-media-export-playlist-root-container.vala \
+       rygel-media-export-playlist-container.vala \
        rygel-media-export-music-item.vala \
        rygel-media-export-video-item.vala \
        rygel-media-export-photo-item.vala \
diff --git a/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala 
b/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala
index 67c8730..6695ea7 100644
--- a/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala
+++ b/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala
@@ -119,6 +119,9 @@ internal class Rygel.MediaExport.MediaCacheUpgrader {
                 case 12:
                     update_v12_v13 ();
                     break;
+                case 13:
+                    this.update_v13_v14 ();
+                    break;
                 default:
                     warning ("Cannot upgrade");
                     database = null;
@@ -479,4 +482,23 @@ internal class Rygel.MediaExport.MediaCacheUpgrader {
             this.database = null;
         }
     }
+
+    private void update_v13_v14 () {
+        try {
+            this.database.begin ();
+
+            this.database.exec ("ALTER TABLE Object ADD COLUMN reference_id " +
+                                "DEFAULT NULL");
+            this.database.exec (this.sql.make (SQLString.TRIGGER_REFERENCE));
+
+            this.database.exec ("UPDATE schema_info SET version = '14'");
+            this.database.commit ();
+            this.database.exec ("VACUUM");
+            this.database.analyze ();
+        } catch (DatabaseError error) {
+            this.database.rollback ();
+            warning ("Database upgrade failed: %s", error.message);
+            this.database = null;
+        }
+    }
 }
diff --git a/src/plugins/media-export/rygel-media-export-media-cache.vala 
b/src/plugins/media-export/rygel-media-export-media-cache.vala
index e904cca..4e43a53 100644
--- a/src/plugins/media-export/rygel-media-export-media-cache.vala
+++ b/src/plugins/media-export/rygel-media-export-media-cache.vala
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009,2010 Jens Georg <mail jensge org>.
+ * Copyright (C) 2013 Intel Corporation.
  *
  * Author: Jens Georg <mail jensge org>
  *
@@ -726,7 +727,8 @@ public class Rygel.MediaExport.MediaCache : Object {
                                 object.object_update_id,
                                 -1,
                                 -1,
-                                is_guarded ? 1 : 0
+                                is_guarded ? 1 : 0,
+                                object.ref_id ?? null
                               };
         if (object is MediaContainer) {
             var container = object as MediaContainer;
@@ -767,6 +769,7 @@ public class Rygel.MediaExport.MediaCache : Object {
             db.exec (this.sql.make (SQLString.TABLE_CLOSURE));
             db.exec (this.sql.make (SQLString.INDEX_COMMON));
             db.exec (this.sql.make (SQLString.TRIGGER_CLOSURE));
+            db.exec (this.sql.make (SQLString.TRIGGER_REFERENCE));
             db.commit ();
             db.analyze ();
             this.save_reset_token (UUID.get ());
@@ -836,6 +839,7 @@ public class Rygel.MediaExport.MediaCache : Object {
             }
             object.object_update_id = (uint) statement.column_int64
                                         (DetailColumn.OBJECT_UPDATE_ID);
+            object.ref_id = statement.column_text (DetailColumn.REFERENCE_ID);
         }
 
         return object;
@@ -949,7 +953,7 @@ public class Rygel.MediaExport.MediaCache : Object {
                 column = "m.duration";
                 break;
             case "@refID":
-                column = "NULL";
+                column = "o.reference_id";
                 break;
             case "@id":
                 column = "o.upnp_id";
diff --git a/src/plugins/media-export/rygel-media-export-object-factory.vala 
b/src/plugins/media-export/rygel-media-export-object-factory.vala
index 1b62107..fd251d7 100644
--- a/src/plugins/media-export/rygel-media-export-object-factory.vala
+++ b/src/plugins/media-export/rygel-media-export-object-factory.vala
@@ -50,6 +50,10 @@ internal class Rygel.MediaExport.ObjectFactory : Object {
             return factory.create_from_hashed_id (id, title);
         }
 
+        if (id.has_prefix ("virtual-parent:" + Rygel.PlaylistItem.UPNP_CLASS)) {
+            return new PlaylistRootContainer (id, title);
+        }
+
         // Return a suitable container for the top-level virtual folders.
         // This corresponds to the short-lived NullContainers that
         // we used to save these in the database.
@@ -61,6 +65,10 @@ internal class Rygel.MediaExport.ObjectFactory : Object {
             return new TrackableDbContainer (id, title);
         }
 
+        if (id.has_prefix ("playlist:")) {
+            return new PlaylistContainer (id, title);
+        }
+
         // Return a writable container for anything with a URI,
         // such as child folders of the file system,
         // to allow uploads.
diff --git a/src/plugins/media-export/rygel-media-export-playlist-container.vala 
b/src/plugins/media-export/rygel-media-export-playlist-container.vala
new file mode 100644
index 0000000..9c89b44
--- /dev/null
+++ b/src/plugins/media-export/rygel-media-export-playlist-container.vala
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * 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;
+
+internal class Rygel.MediaExport.PlaylistContainer : DBContainer,
+                                                     Rygel.WritableContainer {
+    internal static const string URI = WritableContainer.WRITABLE_SCHEME +
+                                       "playlist-container";
+    public ArrayList<string> create_classes { get; set; }
+
+    public PlaylistContainer (string id, string title) {
+        Object (id : id,
+                title : title,
+                parent : null,
+                child_count : 0);
+    }
+
+    public override void constructed () {
+        base.constructed ();
+
+        this.upnp_class = Rygel.MediaContainer.PLAYLIST;
+        this.create_classes = new ArrayList<string> ();
+        // Only items, no folders
+        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);
+
+        // Need to add an URI otherwise core doesn't mark the container as
+        // writable
+        this.uris.add (PlaylistContainer.URI);
+    }
+
+    public async string add_reference (Rygel.MediaObject object,
+                                       Cancellable?      cancellable)
+                                       throws Error {
+        if (object is MediaContainer) {
+            throw new WritableContainerError.NOT_IMPLEMENTED
+                                        ("Cannot create references to containers");
+        }
+
+        object.parent = this;
+
+        // If the original is already a ref_id, point to the original item as
+        // we should not daisy-chain reference items.
+        if (object.ref_id == null) {
+            object.ref_id = object.id;
+        }
+        object.id = UUID.get ();
+
+        var cache = MediaCache.get_default ();
+        cache.save_item (object as MediaItem);
+
+        return object.id;
+    }
+
+
+    public async void add_item (Rygel.MediaItem item,
+                                Cancellable?    cancellable)
+                                throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("Can't create items in %s"),
+                                         this.id);
+    }
+
+    public async void remove_item (string id,
+                                   Cancellable?    cancellable)
+                                   throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("Can't remove items in %s"),
+                                         this.id);
+    }
+
+    public async void add_container (Rygel.MediaContainer container,
+                                     Cancellable?         cancellable)
+                                     throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("Can't add containers in %s"),
+                                         this.id);
+    }
+
+    public async void remove_container (string id,
+                                        Cancellable?    cancellable)
+                                        throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("Can't remove containers in %s"),
+                                         this.id);
+    }
+
+}
diff --git a/src/plugins/media-export/rygel-media-export-playlist-root-container.vala 
b/src/plugins/media-export/rygel-media-export-playlist-root-container.vala
new file mode 100644
index 0000000..5bef6b4
--- /dev/null
+++ b/src/plugins/media-export/rygel-media-export-playlist-root-container.vala
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * 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;
+
+/**
+ * Special class for the toplevel virtual container which aggregates all
+ * playlists.
+ *
+ * This is a special class compared to the the other virtual classes as it
+ * allows the creation of subfolders to create server-side playlists.
+ * It does not allow adding or removal of items, only container adding and
+ * removal.
+ */
+internal class Rygel.MediaExport.PlaylistRootContainer : Rygel.WritableContainer,
+                                                         DBContainer {
+    internal static const string URI = WritableContainer.WRITABLE_SCHEME +
+                                       "playlist-root";
+    public ArrayList<string> create_classes { get; set; }
+
+    public PlaylistRootContainer (string id, string title) {
+        Object (id : id,
+                title : title,
+                parent : null,
+                child_count : 0);
+    }
+
+    public override void constructed () {
+        base.constructed ();
+
+        // We don't support adding real folders here, just playlist container
+        this.create_classes = new ArrayList<string> ();
+        this.create_classes.add (Rygel.MediaContainer.PLAYLIST);
+
+        // Need to add an URI otherwise core doesn't mark the container as
+        // writable
+        this.uris.add (PlaylistRootContainer.URI);
+    }
+
+    public async void add_item (Rygel.MediaItem item,
+                                Cancellable?    cancellable)
+                                throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("Can't create items in %s"),
+                                         this.id);
+    }
+
+    public async void remove_item (string id,
+                                   Cancellable? cancellable)
+                                   throws Error {
+        throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("Can't remove items in %s"),
+                                         this.id);
+   }
+
+    public async void add_container (Rygel.MediaContainer container,
+                                     Cancellable?         cancellable)
+                                     throws Error {
+        if (container.upnp_class != Rygel.MediaContainer.PLAYLIST) {
+            throw new WritableContainerError.NOT_IMPLEMENTED
+                                        (_("upnp:class not supported in %s"),
+                                         this.id);
+        }
+        container.id = "playlist:" + UUID.get ();
+
+        this.media_db.save_container (container);
+        this.media_db.make_object_guarded (container);
+    }
+
+    public async void remove_container (string id,
+                                        Cancellable?    cancellable)
+                                        throws Error {
+        this.media_db.remove_by_id (id);
+    }
+ }
diff --git a/src/plugins/media-export/rygel-media-export-sql-factory.vala 
b/src/plugins/media-export/rygel-media-export-sql-factory.vala
index 42b6201..db3db40 100644
--- a/src/plugins/media-export/rygel-media-export-sql-factory.vala
+++ b/src/plugins/media-export/rygel-media-export-sql-factory.vala
@@ -47,7 +47,8 @@ internal enum Rygel.MediaExport.DetailColumn {
     DISC,
     OBJECT_UPDATE_ID,
     DELETED_CHILD_COUNT,
-    CONTAINER_UPDATE_ID
+    CONTAINER_UPDATE_ID,
+    REFERENCE_ID
 }
 
 internal enum Rygel.MediaExport.SQLString {
@@ -76,7 +77,8 @@ internal enum Rygel.MediaExport.SQLString {
     MAX_UPDATE_ID,
     MAKE_GUARDED,
     IS_GUARDED,
-    UPDATE_GUARDED_OBJECT
+    UPDATE_GUARDED_OBJECT,
+    TRIGGER_REFERENCE
 }
 
 internal class Rygel.MediaExport.SQLFactory : Object {
@@ -93,8 +95,8 @@ internal class Rygel.MediaExport.SQLFactory : Object {
     "INSERT OR REPLACE INTO Object " +
         "(upnp_id, title, type_fk, parent, timestamp, uri, " +
          "object_update_id, deleted_child_count, container_update_id, " +
-         "is_guarded) VALUES " +
-        "(?,?,?,?,?,?,?,?,?,?)";
+         "is_guarded, reference_id) VALUES " +
+        "(?,?,?,?,?,?,?,?,?,?,?)";
 
     private const string UPDATE_GUARDED_OBJECT_STRING =
     "UPDATE Object SET " +
@@ -117,7 +119,7 @@ internal class Rygel.MediaExport.SQLFactory : Object {
     "m.sample_freq, m.bits_per_sample, m.channels, m.track, " +
     "m.color_depth, m.duration, o.upnp_id, o.parent, o.timestamp, " +
     "o.uri, m.dlna_profile, m.genre, m.disc, o.object_update_id, " +
-    "o.deleted_child_count, o.container_update_id ";
+    "o.deleted_child_count, o.container_update_id, o.reference_id ";
 
     private const string GET_OBJECT_WITH_PATH =
     "SELECT DISTINCT " + ALL_DETAILS_STRING +
@@ -185,7 +187,7 @@ internal class Rygel.MediaExport.SQLFactory : Object {
         "WHERE _column IS NOT NULL %s ORDER BY _column COLLATE CASEFOLD " +
     "LIMIT ?,?";
 
-    internal const string SCHEMA_VERSION = "13";
+    internal const string SCHEMA_VERSION = "14";
     internal const string CREATE_META_DATA_TABLE_STRING =
     "CREATE TABLE meta_data (size INTEGER NOT NULL, " +
                             "mime_type TEXT NOT NULL, " +
@@ -223,7 +225,8 @@ internal class Rygel.MediaExport.SQLFactory : Object {
                           "object_update_id INTEGER, " +
                           "deleted_child_count INTEGER, " +
                           "container_update_id INTEGER, " +
-                          "is_guarded INTEGER);" +
+                          "is_guarded INTEGER, " +
+                          "reference_id TEXT DEFAULT NULL);" +
     "INSERT INTO schema_info (version) VALUES ('" +
     SQLFactory.SCHEMA_VERSION + "'); ";
 
@@ -259,6 +262,13 @@ internal class Rygel.MediaExport.SQLFactory : Object {
         "DELETE FROM meta_data WHERE meta_data.object_fk = OLD.upnp_id; "+
     "END;";
 
+    private const string DELETE_REFERENCE_TRIGGER_STRING =
+    "CREATE TRIGGER trgr_delete_references " +
+    "BEFORE DELETE ON Object " +
+    "FOR EACH ROW BEGIN " +
+        "DELETE FROM Object WHERE OLD.upnp_id = Object.reference_id; " +
+    "END;";
+
     private const string CREATE_INDICES_STRING =
     "CREATE INDEX IF NOT EXISTS idx_parent on Object(parent);" +
     "CREATE INDEX IF NOT EXISTS idx_object_upnp_id on Object(upnp_id);" +
@@ -348,6 +358,8 @@ internal class Rygel.MediaExport.SQLFactory : Object {
                 return IS_GUARDED_STRING;
             case SQLString.UPDATE_GUARDED_OBJECT:
                 return UPDATE_GUARDED_OBJECT_STRING;
+            case SQLString.TRIGGER_REFERENCE:
+                return DELETE_REFERENCE_TRIGGER_STRING;
             default:
                 assert_not_reached ();
         }
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 023d3be..2e0a272 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
@@ -56,8 +56,13 @@ internal class Rygel.MediaExport.WritableDbContainer : TrackableDbContainer,
         this.create_classes.add (Rygel.MediaContainer.STORAGE_FOLDER);
     }
 
-    public async void add_item (Rygel.MediaItem item, Cancellable? cancellable)
-                                throws Error {
+    public virtual async void add_item (Rygel.MediaItem item,
+                                        Cancellable? cancellable)
+                                        throws Error {
+        if (item.id == null && item.ref_id != null) {
+            warning ("=> CreateReference not supported");
+            throw new WritableContainerError.NOT_IMPLEMENTED ("Not supported");
+        }
         item.parent = this;
         var file = File.new_for_uri (item.uris[0]);
         // TODO: Mark as place-holder. Make this proper some time.
@@ -69,9 +74,9 @@ internal class Rygel.MediaExport.WritableDbContainer : TrackableDbContainer,
         this.media_db.make_object_guarded (item);
     }
 
-    public async void add_container (MediaContainer container,
-                                     Cancellable?   cancellable)
-                                     throws Error {
+    public virtual async void add_container (MediaContainer container,
+                                             Cancellable?   cancellable)
+                                             throws Error {
         container.parent = this;
         switch (container.upnp_class) {
         case MediaContainer.STORAGE_FOLDER:
@@ -91,15 +96,16 @@ internal class Rygel.MediaExport.WritableDbContainer : TrackableDbContainer,
         this.media_db.make_object_guarded (container);
     }
 
-    public async void remove_item (string id, Cancellable? cancellable)
-                                   throws Error {
+    public virtual 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 {
+    public virtual async void remove_container (string id,
+                                                Cancellable? cancellable)
+                                                throws Error {
         throw new WritableContainerError.NOT_IMPLEMENTED ("Not supported");
     }
 


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