[gupnp-av/wip/phako/clocksync] wip: Implement BOOKMARKS feature




commit 03d996353cf6eca52ce28e37b5dc560605d110e2
Author: Jens Georg <mail jensge org>
Date:   Thu Oct 20 00:38:05 2022 +0200

    wip: Implement BOOKMARKS feature

 libgupnp-av/gupnp-didl-lite-createclass.h       |   1 -
 libgupnp-av/gupnp-feature-clocksync.c           |   0
 libgupnp-av/gupnp-feature-clocksync.h           |   0
 libgupnp-av/gupnp-feature-container-shortcuts.c | 124 ++++++++++++++++++++++++
 libgupnp-av/gupnp-feature-container-shortcuts.h |  52 ++++++++++
 libgupnp-av/gupnp-feature-list-parser.c         |  18 +++-
 libgupnp-av/gupnp-feature-private.h             |  19 ++++
 libgupnp-av/gupnp-feature.c                     |  82 +++++++++++++++-
 libgupnp-av/meson.build                         |   1 +
 libgupnp-av/xml-util.c                          |   6 ++
 tests/check-feature-list-parser.c               |  41 +++++++-
 11 files changed, 334 insertions(+), 10 deletions(-)
---
diff --git a/libgupnp-av/gupnp-didl-lite-createclass.h b/libgupnp-av/gupnp-didl-lite-createclass.h
index 3d33f9e..3f734ea 100644
--- a/libgupnp-av/gupnp-didl-lite-createclass.h
+++ b/libgupnp-av/gupnp-didl-lite-createclass.h
@@ -15,7 +15,6 @@
 
 #include <glib-object.h>
 #include <libxml/tree.h>
-#include "gupnp-didl-lite-container.h"
 
 G_BEGIN_DECLS
 
diff --git a/libgupnp-av/gupnp-feature-clocksync.c b/libgupnp-av/gupnp-feature-clocksync.c
new file mode 100644
index 0000000..e69de29
diff --git a/libgupnp-av/gupnp-feature-clocksync.h b/libgupnp-av/gupnp-feature-clocksync.h
new file mode 100644
index 0000000..e69de29
diff --git a/libgupnp-av/gupnp-feature-container-shortcuts.c b/libgupnp-av/gupnp-feature-container-shortcuts.c
new file mode 100644
index 0000000..8dffd6d
--- /dev/null
+++ b/libgupnp-av/gupnp-feature-container-shortcuts.c
@@ -0,0 +1,124 @@
+#include "gupnp-feature-container-shortcuts.h"
+#include "gupnp-feature-private.h"
+#include "xml-util.h"
+
+#include <libxml/tree.h>
+
+struct _GUPnPContainerShortcut {
+        GUPnPAVXMLDoc *doc;
+        xmlNodePtr node;
+};
+
+
+struct _GUPnPFeatureContainerShortcuts {
+        GUPnPFeature parent;
+};
+
+G_DEFINE_TYPE (GUPnPFeatureContainerShortcuts,
+               gupnp_feature_container_shortcuts,
+               GUPNP_TYPE_FEATURE)
+
+static void
+gupnp_feature_container_shortcuts_init (GUPnPFeatureContainerShortcuts *self)
+{
+}
+
+static void
+gupnp_feature_container_shortcuts_class_init (
+        GUPnPFeatureContainerShortcutsClass *klass)
+{
+}
+
+/**
+ * gupnp_feature_container_shortcuts_get_list:
+ *
+ * Get the supported shortcuts on that feature.
+ *
+ * Returns: (element-type: GUPnPContainerShortcut): List
+ * of shortcuts included in this feature.
+ */
+GSList *
+gupnp_feature_container_shortcuts_get_list (
+        GUPnPFeatureContainerShortcuts *self)
+{
+        xmlNode *node = gupnp_feature_get_node (GUPNP_FEATURE (self));
+        xmlNode *shortcuts_node =
+                av_xml_util_get_element (node, "shortcutlist", NULL);
+
+        if (shortcuts_node == NULL) {
+                return NULL;
+        }
+
+        GList *shortcuts =
+                av_xml_util_get_child_elements_by_name (shortcuts_node,
+                                                        "shortcut");
+        GList *it = shortcuts;
+        GSList *retval = NULL;
+
+        while (it != NULL) {
+                GUPnPContainerShortcut *shortcut =
+                        g_rc_box_new0 (GUPnPContainerShortcut);
+
+                shortcut->doc = gupnp_feature_get_doc (GUPNP_FEATURE (self));
+                shortcut->node = it->data;
+
+                retval = g_slist_prepend (retval, shortcut);
+                it = it->next;
+        }
+
+        return g_slist_reverse(retval);
+}
+
+/**
+ * gupnp_container_shortcut_type:
+ *
+ * Get the gtype for GUPnPServiceActon
+ *
+ * Return value: The gtype of GUPnPServiceAction
+ **/
+GType
+gupnp_container_shortcut_get_type (void)
+{
+        static GType our_type = 0;
+
+        if (our_type == 0)
+                our_type = g_boxed_type_register_static (
+                        "GUPnPServiceAction",
+                        (GBoxedCopyFunc) gupnp_container_shortcut_ref,
+                        (GBoxedFreeFunc) gupnp_container_shortcut_unref);
+
+        return our_type;
+}
+
+GUPnPContainerShortcut *
+gupnp_container_shortcut_ref (GUPnPContainerShortcut *shortcut)
+{
+        return g_rc_box_acquire (shortcut);
+}
+
+static void
+gupnp_container_shortcut_free (GUPnPContainerShortcut *shortcut)
+{
+        g_clear_pointer (&shortcut->doc, av_xml_doc_unref);
+}
+
+void
+gupnp_container_shortcut_unref (GUPnPContainerShortcut *shortcut)
+{
+        g_rc_box_release_full (shortcut,
+                               (GDestroyNotify) gupnp_container_shortcut_free);
+}
+
+
+const char *
+gupnp_container_shortcut_get_name (GUPnPContainerShortcut *shortcut)
+{
+        return av_xml_util_get_child_element_content (shortcut->node, "name");
+}
+
+const char *
+gupnp_container_shortcut_get_object_id (GUPnPContainerShortcut *shortcut)
+{
+        return av_xml_util_get_child_element_content (shortcut->node,
+                                                      "objectID");
+}
diff --git a/libgupnp-av/gupnp-feature-container-shortcuts.h b/libgupnp-av/gupnp-feature-container-shortcuts.h
new file mode 100644
index 0000000..5fb475d
--- /dev/null
+++ b/libgupnp-av/gupnp-feature-container-shortcuts.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2022 Jens Georg <mail jensge org>
+
+#pragma once
+
+#include <config.h>
+
+#include "gupnp-feature.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GUPNP_TYPE_FEATURE_CONTAINER_SHORTCUTS                                 \
+        (gupnp_feature_container_shortcuts_get_type ())
+
+G_DECLARE_FINAL_TYPE (GUPnPFeatureContainerShortcuts,
+                      gupnp_feature_container_shortcuts,
+                      GUPNP,
+                      FEATURE_CONTAINER_SHORTCUTS,
+                      GUPnPFeature)
+
+typedef struct _GUPnPContainerShortcut GUPnPContainerShortcut;
+
+#define GUPNP_TYPE_CONTAINER_SHORTCUT (gupnp_container_shortcut_get_type ())
+
+struct _GUPnPFeatureContainerShortcutsClass {
+        GUPnPFeature parent_class;
+};
+
+GSList *gupnp_feature_container_shortcuts_get_list(
+        GUPnPFeatureContainerShortcuts *self);
+
+GType
+gupnp_container_shortcut_get_type (void);
+
+GUPnPContainerShortcut *
+gupnp_container_shortcut_ref (GUPnPContainerShortcut *);
+
+void
+gupnp_container_shortcut_unref (GUPnPContainerShortcut *);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GUPnPContainerShortcut,
+                               gupnp_container_shortcut_unref)
+
+const char *
+gupnp_container_shortcut_get_name (GUPnPContainerShortcut *);
+
+const char *
+gupnp_container_shortcut_get_object_id (GUPnPContainerShortcut *);
+
+G_END_DECLS
diff --git a/libgupnp-av/gupnp-feature-list-parser.c b/libgupnp-av/gupnp-feature-list-parser.c
index 16208b5..f4fe357 100644
--- a/libgupnp-av/gupnp-feature-list-parser.c
+++ b/libgupnp-av/gupnp-feature-list-parser.c
@@ -113,6 +113,7 @@ gupnp_feature_list_parser_parse_text
         xmlDoc       *doc;
         xmlNode      *element;
         GList        *feature_list = NULL;
+        GUPnPAVXMLDoc *xml_doc = NULL;
 
         doc = xmlRecoverMemory (text, strlen (text));
         if (doc == NULL) {
@@ -124,6 +125,8 @@ gupnp_feature_list_parser_parse_text
                 return NULL;
         }
 
+        xml_doc = av_xml_doc_new (doc);
+
         /* Get a pointer to root element */
         element = av_xml_util_get_element ((xmlNode *) doc, "Features", NULL);
         if (element == NULL) {
@@ -168,9 +171,16 @@ gupnp_feature_list_parser_parse_text
                         object_ids = get_feature_object_ids (element);
 
                         feature = g_object_new (GUPNP_TYPE_FEATURE,
-                                                "name", name,
-                                                "version", version,
-                                                "object-ids", object_ids,
+                                                "doc",
+                                                xml_doc,
+                                                "node",
+                                                element,
+                                                "name",
+                                                name,
+                                                "version",
+                                                version,
+                                                "object-ids",
+                                                object_ids,
                                                 NULL);
 
                         feature_list = g_list_append (feature_list, feature);
@@ -179,7 +189,7 @@ gupnp_feature_list_parser_parse_text
                 }
         }
 
-        xmlFreeDoc (doc);
+        av_xml_doc_unref (xml_doc);
 
         return feature_list;
 }
diff --git a/libgupnp-av/gupnp-feature-private.h b/libgupnp-av/gupnp-feature-private.h
new file mode 100644
index 0000000..908ac5d
--- /dev/null
+++ b/libgupnp-av/gupnp-feature-private.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2022 Jens Georg <mail jensge org>
+
+#pragma once
+
+#include "gupnp-feature.h"
+#include "xml-util.h"
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+GUPnPAVXMLDoc *
+gupnp_feature_get_doc (GUPnPFeature *feature);
+
+G_GNUC_INTERNAL
+xmlNode *
+gupnp_feature_get_node (GUPnPFeature *feature);
+
+G_END_DECLS
diff --git a/libgupnp-av/gupnp-feature.c b/libgupnp-av/gupnp-feature.c
index ee12015..0f3f566 100644
--- a/libgupnp-av/gupnp-feature.c
+++ b/libgupnp-av/gupnp-feature.c
@@ -17,11 +17,18 @@
 #include <config.h>
 
 #include "gupnp-feature.h"
+#include "gupnp-feature-private.h"
+
+#include "xml-util.h"
+
+#include <libxml/tree.h>
 
 struct _GUPnPFeaturePrivate {
         char *name;
         char *version;
         char *object_ids;
+        GUPnPAVXMLDoc *doc;
+        xmlNode *node;
 };
 typedef struct _GUPnPFeaturePrivate GUPnPFeaturePrivate;
 
@@ -33,7 +40,9 @@ enum {
         PROP_0,
         PROP_NAME,
         PROP_VERSION,
-        PROP_OBJECT_IDS
+        PROP_OBJECT_IDS,
+        PROP_DOC,
+        PROP_NODE
 };
 
 static void
@@ -78,6 +87,12 @@ gupnp_feature_get_property (GObject    *object,
                 g_value_set_string (value,
                                     gupnp_feature_get_object_ids (feature));
                 break;
+        case PROP_DOC:
+                g_value_set_boxed (value, gupnp_feature_get_doc (feature));
+                break;
+        case PROP_NODE:
+                g_value_set_pointer (value, gupnp_feature_get_node (feature));
+                break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                 break;
@@ -101,7 +116,16 @@ gupnp_feature_set_property (GObject      *object,
                 priv->version = g_value_dup_string (value);
                 break;
         case PROP_OBJECT_IDS:
-                priv->object_ids = g_value_dup_string (value);
+                if (G_VALUE_HOLDS(value, G_TYPE_STRING))
+                        priv->object_ids = g_value_dup_string (value);
+                else
+                        priv->object_ids = NULL;
+                break;
+        case PROP_DOC:
+                priv->doc = g_value_dup_boxed (value);
+                break;
+        case PROP_NODE:
+                priv->node = g_value_get_pointer (value);
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -173,6 +197,37 @@ gupnp_feature_class_init (GUPnPFeatureClass *klass)
                                       G_PARAM_STATIC_NAME |
                                       G_PARAM_STATIC_NICK |
                                       G_PARAM_STATIC_BLURB));
+
+        /**
+         * GUPnPFeature:doc:
+         *
+         * The XML Doc of the feature list that contains this feature.
+         **/
+        g_object_class_install_property (
+                object_class,
+                PROP_DOC,
+                g_param_spec_boxed ("doc",
+                                    "XML document",
+                                    "XML document for the feature list that "
+                                    "contains this feature",
+                                    av_xml_doc_get_type (),
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                            G_PARAM_STATIC_STRINGS));
+
+        /**
+         * GUPnPFeature:node:
+         *
+         * The XML node for this feature
+         **/
+        g_object_class_install_property (
+                object_class,
+                PROP_OBJECT_IDS,
+                g_param_spec_pointer ("node",
+                                      "XML node",
+                                      "XML node of this feature",
+                                      G_PARAM_READWRITE |
+                                              G_PARAM_CONSTRUCT_ONLY |
+                                              G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -209,6 +264,24 @@ gupnp_feature_get_version (GUPnPFeature *feature)
         return priv->version;
 }
 
+GUPnPAVXMLDoc *
+gupnp_feature_get_doc (GUPnPFeature *feature)
+{
+        GUPnPFeaturePrivate *priv =
+                gupnp_feature_get_instance_private (GUPNP_FEATURE (feature));
+
+        return av_xml_doc_ref (priv->doc);
+}
+
+xmlNode *
+gupnp_feature_get_node (GUPnPFeature *feature)
+{
+        GUPnPFeaturePrivate *priv =
+                gupnp_feature_get_instance_private (GUPNP_FEATURE (feature));
+
+        return priv->node;
+}
+
 /**
  * gupnp_feature_get_object_ids:
  * @feature: #GUPnPFeature
@@ -216,8 +289,11 @@ gupnp_feature_get_version (GUPnPFeature *feature)
  * Get the object IDs related to the @feature.
  *
  * Return value: The object IDs related to the @feature.
+ *
+ * Deprecated. Use gupnp_feature_bookmarks_get_object_ids() or
+ * gupnp_feature_epg_get_object_ids() instead
  **/
-const char *
+G_GNUC_DEPRECATED const char *
 gupnp_feature_get_object_ids (GUPnPFeature *feature)
 {
         GUPnPFeaturePrivate *priv =
diff --git a/libgupnp-av/meson.build b/libgupnp-av/meson.build
index 89cc927..769c886 100644
--- a/libgupnp-av/meson.build
+++ b/libgupnp-av/meson.build
@@ -16,6 +16,7 @@ introspection_sources = [
     'gupnp-didl-lite-writer.c',
     'gupnp-dlna.c',
     'gupnp-feature.c',
+    'gupnp-feature-container-shortcuts.c',
     'gupnp-feature-list-parser.c',
     'gupnp-last-change-parser.c',
     'gupnp-media-collection.c',
diff --git a/libgupnp-av/xml-util.c b/libgupnp-av/xml-util.c
index 0d1661b..a72ad00 100644
--- a/libgupnp-av/xml-util.c
+++ b/libgupnp-av/xml-util.c
@@ -54,6 +54,12 @@ av_xml_doc_free (GUPnPAVXMLDoc *doc)
         g_clear_pointer (&doc->doc, xmlFreeDoc);
 }
 
+GUPnPAVXMLDoc *
+av_xml_doc_ref (GUPnPAVXMLDoc *doc)
+{
+        return g_rc_box_acquire (doc);
+}
+
 void
 av_xml_doc_unref (GUPnPAVXMLDoc *doc)
 {
diff --git a/tests/check-feature-list-parser.c b/tests/check-feature-list-parser.c
index 94e968a..7011a67 100644
--- a/tests/check-feature-list-parser.c
+++ b/tests/check-feature-list-parser.c
@@ -10,17 +10,21 @@
 #include <config.h>
 
 #include <libgupnp-av/gupnp-feature-list-parser.h>
+#include <libgupnp-av/gupnp-feature-container-shortcuts.h>
+
 #include <stdlib.h>
 #include <string.h>
 
 static const char * const names[] = {
         "BOOKMARK",
         "EPG",
+        "CONTAINER_SHORTCUTS",
 };
 
 static const char * const versions[] = {
         "1",
         "2",
+        "1",
 };
 
 static const char * const ids[] = {
@@ -28,6 +32,17 @@ static const char * const ids[] = {
         "epg1,epg2",
 };
 
+static const char *const container_names[] = {
+        "MUSIC_GENRES",
+        "IMAGES_ALL",
+};
+
+static const char *const container_ids[] = {
+        "container:genre",
+        "container:images_all",
+};
+
+//clang-format off
 static const char *text =
         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
         "<Features "
@@ -43,7 +58,20 @@ static const char *text =
                 "<Feature name=\"EPG\" version=\"2\">"
                         "<objectIDs>epg1,epg2</objectIDs>"
                 "</Feature>"
+                "<Feature name=\"CONTAINER_SHORTCUTS\" version=\"1\">"
+                        "<shortcutlist>"
+                                "<shortcut>"
+                                        "<name>MUSIC_GENRES</name>"
+                                        "<objectID>container:genre</objectID>"
+                                "</shortcut>"
+                                "<shortcut>"
+                                        "<name>IMAGES_ALL</name>"
+                                        "<objectID>container:images_all</objectID>"
+                                "</shortcut>"
+                        "</shortcutlist>"
+                "</Feature>"
         "</Features>";
+// clang-format on
 
 static gboolean
 check_feature (GUPnPFeature *feature)
@@ -54,11 +82,20 @@ check_feature (GUPnPFeature *feature)
                         return FALSE;
 
         if (strcmp (versions[index], gupnp_feature_get_version (feature)))
-                        return FALSE;
+                return FALSE;
 
-        if (strcmp (ids[index], gupnp_feature_get_object_ids (feature)))
+        if (index < 2) {
+                if (strcmp (ids[index], gupnp_feature_get_object_ids (feature)))
+                        return FALSE;
+        } else {
+                if (!GUPNP_IS_FEATURE_CONTAINER_SHORTCUTS (feature))
                         return FALSE;
 
+                GUPnPFeatureContainerShortcuts *f =
+                        GUPNP_FEATURE_CONTAINER_SHORTCUTS (feature);
+
+                GSList *s = gupnp_feature_container_shortcuts_get_list (f);
+        }
         index++;
 
         return TRUE;


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