[grilo-plugins/0.2.x] Add DMAP plugin



commit e6725572932a727c12d3fb1d3a67909dfabbd7b6
Author: W. Michael Petullo <mike flyn org>
Date:   Wed Jul 11 12:46:07 2012 -0500

    Add DMAP plugin
    
    This fixes https://bugzilla.gnome.org/show_bug.cgi?id=652516
    
    Signed-off-by: W. Michael Petullo <mike flyn org>
    Signed-off-by: Juan A. Suarez Romero <jasuarez igalia com>

 configure.ac                          |   40 +++
 src/Makefile.am                       |    8 +-
 src/dmap/Makefile.am                  |   45 ++++
 src/dmap/grl-dmap.c                   |  459 +++++++++++++++++++++++++++++++++
 src/dmap/grl-dmap.h                   |   76 ++++++
 src/dmap/grl-dmap.xml                 |   10 +
 src/dmap/simple-daap-record-factory.c |   58 ++++
 src/dmap/simple-daap-record-factory.h |   72 +++++
 src/dmap/simple-daap-record.c         |  326 +++++++++++++++++++++++
 src/dmap/simple-daap-record.h         |   78 ++++++
 src/dmap/simple-dmap-db.c             |  192 ++++++++++++++
 src/dmap/simple-dmap-db.h             |   82 ++++++
 12 files changed, 1444 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cb55ce6..c57ea6e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -115,6 +115,8 @@ PKG_CHECK_MODULES(GUPNP, gupnp-1.0 >= 0.13, HAVE_GUPNP=yes, HAVE_GUPNP=no)
 
 PKG_CHECK_MODULES(GUPNPAV, gupnp-av-1.0 >= 0.5, HAVE_GUPNPAV=yes, HAVE_GUPNPAV=no)
 
+PKG_CHECK_MODULES(DMAP, libdmapsharing-3.0 >= 2.9.12, HAVE_DMAP=yes, HAVE_DMAP=no)
+
 PKG_CHECK_MODULES(SQLITE, sqlite3, HAVE_SQLITE=yes, HAVE_SQLITE=no)
 
 PKG_CHECK_MODULES(GDATA, libgdata >= 0.7.0, HAVE_GDATA=yes, HAVE_GDATA=no)
@@ -804,6 +806,43 @@ AC_SUBST(LOCALMETADATA_PLUGIN_ID)
 AC_DEFINE_UNQUOTED([LOCALMETADATA_PLUGIN_ID], ["$LOCALMETADATA_PLUGIN_ID"], [Local metadata plugin ID])
 
 # ----------------------------------------------------------
+# BUILD DMAP PLUGIN
+# ----------------------------------------------------------
+
+AC_ARG_ENABLE(dmap,
+        AC_HELP_STRING([--enable-dmap],
+                [enable DMAP plugin (default: auto)]),
+        [
+                case "$enableval" in
+                     yes)
+                        if test "x$HAVE_DMAP" = "xno"; then
+                           AC_MSG_ERROR([DMAP not found, install it or use --disable-dmap])
+                        fi
+                        if test "$xHAVE_XML" = "xno"; then
+                           AC_MSG_ERROR([xml2 not found, install it or use --disable-dmap])
+                        fi
+                esac
+        ],
+        [
+                if test "x$HAVE_DMAP" = "xyes" -a "x$HAVE_XML" = "xyes"; then
+                        enable_dmap=yes
+                else
+                        enable_dmap=no
+                fi
+        ])
+
+AM_CONDITIONAL([DMAP_PLUGIN], [test "x$enable_dmap" = "xyes"])
+GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL dmap"
+if test "x$enable_dmap" = "xyes"
+then
+       GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED dmap"
+fi
+
+DMAP_PLUGIN_ID="grl-dmap"
+AC_SUBST(DMAP_PLUGIN_ID)
+AC_DEFINE_UNQUOTED([DMAP_PLUGIN_ID], ["$DMAP_PLUGIN_ID"], [DMAP plugin ID])
+
+# ----------------------------------------------------------
 # GETTEXT
 # ----------------------------------------------------------
 
@@ -830,6 +869,7 @@ AC_CONFIG_FILES([
   src/apple-trailers/Makefile
   src/bliptv/Makefile
   src/bookmarks/Makefile
+  src/dmap/Makefile
   src/fake-metadata/Makefile
   src/filesystem/Makefile
   src/flickr/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 2089369..4795e02 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,10 @@ if BOOKMARKS_PLUGIN
 SUBDIRS += bookmarks
 endif
 
+if DMAP_PLUGIN
+SUBDIRS += dmap
+endif
+
 if FAKEMETADATA_PLUGIN
 SUBDIRS += fake-metadata
 endif
@@ -81,8 +85,8 @@ SUBDIRS += youtube
 endif
 
 DIST_SUBDIRS = \
-	apple-trailers bliptv bookmarks fake-metadata filesystem flickr	\
-	gravatar jamendo lastfm-albumart local-metadata metadata-store		\
+	apple-trailers bliptv bookmarks dmap fake-metadata filesystem flickr	\
+	gravatar jamendo lastfm-albumart local-metadata metadata-store			\
 	optical-media podcasts shoutcast tracker upnp vimeo youtube
 
 MAINTAINERCLEANFILES =	\
diff --git a/src/dmap/Makefile.am b/src/dmap/Makefile.am
new file mode 100644
index 0000000..a5af68b
--- /dev/null
+++ b/src/dmap/Makefile.am
@@ -0,0 +1,45 @@
+#
+# Makefile.am
+#
+# Author: W. Michael Petullo <mike flyn org>
+#
+# Copyright (C) 2011 W. Michael Petullo. All rights reserved.
+
+ext_LTLIBRARIES	= libgrldmap.la
+
+libgrldmap_la_CFLAGS =	\
+	$(DEPS_CFLAGS)			\
+	$(DMAP_CFLAGS)			\
+	$(XML_CFLAGS)
+
+libgrldmap_la_LIBADD =	\
+	$(DEPS_LIBS)			\
+	$(DMAP_LIBS)			\
+	$(XML_LIBS)
+
+libgrldmap_la_LDFLAGS =	\
+	-no-undefined			\
+	-module					\
+	-avoid-version
+
+libgrldmap_la_SOURCES =				\
+	grl-dmap.c							\
+	grl-dmap.h							\
+	simple-daap-record.c				\
+	simple-daap-record.h				\
+	simple-daap-record-factory.c	\
+	simple-daap-record-factory.h	\
+	simple-dmap-db.c					\
+	simple-dmap-db.h
+
+extdir			= $(GRL_PLUGINS_DIR)
+dmapxmldir		= $(GRL_PLUGINS_DIR)
+dmapxml_DATA	= $(DMAP_PLUGIN_ID).xml
+
+EXTRA_DIST      = $(dmapxml_DATA)
+
+MAINTAINERCLEANFILES =	\
+	*.in						\
+	*~
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
diff --git a/src/dmap/grl-dmap.c b/src/dmap/grl-dmap.c
new file mode 100644
index 0000000..3e82ea6
--- /dev/null
+++ b/src/dmap/grl-dmap.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2011 W. Michael Petullo.
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Contact: W. Michael Petullo <mike flyn org>
+ *
+ * This library 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; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <grilo.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libdmapsharing/dmap.h>
+
+#include "grl-dmap.h"
+#include "simple-dmap-db.h"
+#include "simple-daap-record.h"
+#include "simple-daap-record-factory.h"
+
+/* --------- Logging  -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT dmap_log_domain
+GRL_LOG_DOMAIN_STATIC(dmap_log_domain);
+
+/* --- Plugin information --- */
+
+#define PLUGIN_ID   DMAP_PLUGIN_ID
+
+#define SOURCE_ID_TEMPLATE   "grl-dmap-%s"
+#define SOURCE_DESC_TEMPLATE "A source for browsing the DMAP server '%s'"
+
+/* --- Grilo DMAP Private --- */
+
+#define GRL_DMAP_SOURCE_GET_PRIVATE(object)           \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),              \
+                               GRL_DMAP_SOURCE_TYPE,  \
+                               GrlDmapSourcePrivate))
+
+struct _GrlDmapSourcePrivate {
+  DMAPMdnsBrowserService *service;
+};
+
+/* --- Data types --- */
+
+typedef struct _ResultCbAndArgs {
+  GrlSourceResultCb callback;
+  GrlSource *source;
+  guint op_id;
+  GHRFunc predicate;
+  gchar *predicate_data;
+  guint skip;
+  guint count;
+  guint remaining;
+  gpointer user_data;
+} ResultCbAndArgs;
+
+typedef struct _ResultCbAndArgsAndDb {
+  ResultCbAndArgs cb;
+  SimpleDMAPDb *db;
+} ResultCbAndArgsAndDb;
+
+static GrlDmapSource *grl_dmap_source_new (DMAPMdnsBrowserService *service);
+
+static void grl_dmap_source_finalize (GObject *object);
+
+gboolean grl_dmap_plugin_init (GrlRegistry *registry,
+                               GrlPlugin *plugin,
+                               GList *configs);
+
+static const GList *grl_dmap_source_supported_keys (GrlSource *source);
+
+static void grl_dmap_source_browse (GrlSource *source,
+                                    GrlSourceBrowseSpec *bs);
+
+static void grl_dmap_source_search (GrlSource *source,
+                                    GrlSourceSearchSpec *ss);
+
+
+static void service_added_cb (DMAPMdnsBrowser *browser,
+                              DMAPMdnsBrowserService *service,
+                              GrlPlugin *plugin);
+
+static void service_removed_cb (DMAPMdnsBrowser *browser,
+                                const gchar *service_name,
+                                GrlPlugin *plugin);
+
+/* ===================== Globals  ======================= */
+static DMAPMdnsBrowser *browser;
+static GHashTable *connections; // Maps URIs to DBs.
+static GHashTable *sources;     // Map DMAP services to Grilo media sources.
+
+/* =================== DMAP Plugin ====================== */
+
+gboolean
+grl_dmap_plugin_init (GrlRegistry *registry,
+                      GrlPlugin *plugin,
+                      GList *configs)
+{
+  GError *error = NULL;
+
+  GRL_LOG_DOMAIN_INIT (dmap_log_domain, "dmap");
+
+  GRL_DEBUG ("dmap_plugin_init");
+
+  browser     = dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP);
+  connections = g_hash_table_new (g_str_hash, g_str_equal);
+  sources     = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+  g_signal_connect (G_OBJECT (browser),
+                    "service-added",
+                    G_CALLBACK (service_added_cb),
+                    (gpointer) plugin);
+
+  g_signal_connect (G_OBJECT (browser),
+                    "service-removed",
+                    G_CALLBACK (service_removed_cb),
+                    (gpointer) plugin);
+
+  dmap_mdns_browser_start (browser, &error);
+  if (error) {
+    g_warning ("error starting browser. code: %d message: %s",
+               error->code,
+               error->message);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+GRL_PLUGIN_REGISTER (grl_dmap_plugin_init,
+                     NULL,
+                     PLUGIN_ID);
+
+/* ================== DMAP GObject ====================== */
+
+G_DEFINE_TYPE (GrlDmapSource,
+               grl_dmap_source,
+               GRL_TYPE_SOURCE);
+
+static GrlDmapSource *
+grl_dmap_source_new (DMAPMdnsBrowserService *service)
+{
+  gchar *source_desc;
+  GrlDmapSource *source;
+
+  GRL_DEBUG ("grl_dmap_source_new");
+  source_desc = g_strdup_printf (SOURCE_DESC_TEMPLATE, service->name);
+
+  source = g_object_new (GRL_DMAP_SOURCE_TYPE,
+                         "source-id",   service->name,
+                         "source-name", service->name,
+                         "source-desc", source_desc,
+                         NULL);
+
+  source->priv->service = service;
+
+  g_free (source_desc);
+
+  return source;
+}
+
+static void
+grl_dmap_source_class_init (GrlDmapSourceClass * klass)
+{
+  GrlSourceClass *source_class = GRL_SOURCE_CLASS (klass);
+
+  source_class->browse = grl_dmap_source_browse;
+  source_class->search = grl_dmap_source_search;
+  source_class->supported_keys = grl_dmap_source_supported_keys;
+
+  G_OBJECT_CLASS (source_class)->finalize = grl_dmap_source_finalize;
+
+  g_type_class_add_private (klass, sizeof (GrlDmapSourcePrivate));
+}
+
+static void
+grl_dmap_source_init (GrlDmapSource *source)
+{
+  source->priv = GRL_DMAP_SOURCE_GET_PRIVATE (source);
+}
+
+static void
+grl_dmap_source_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (grl_dmap_source_parent_class)->finalize (object);
+}
+
+/* ======================= Utilities ==================== */
+
+static gchar *
+build_url (DMAPMdnsBrowserService *service)
+{
+  return g_strdup_printf ("%s://%s:%u",
+                          service->service_name,
+                          service->host,
+                          service->port);
+}
+
+static void
+add_media_from_service (gpointer id,
+                        DAAPRecord *record,
+                        ResultCbAndArgs *cb)
+{
+  gchar *id_s   = NULL,
+    *title  = NULL,
+    *url    = NULL;
+  int duration  = 0;
+  gboolean has_video;
+  GrlMedia *media;
+
+  g_object_get (record,
+                "title",
+                &title,
+                "location",
+                &url,
+                "has-video",
+                &has_video,
+                "duration",
+                &duration,
+                NULL);
+
+  id_s = g_strdup_printf ("%u", GPOINTER_TO_UINT (id));
+
+  if (has_video == TRUE) {
+    media = grl_media_video_new ();
+  } else {
+    media = grl_media_audio_new ();
+  }
+
+  grl_media_set_id       (media, id_s);
+  grl_media_set_duration (media, duration);
+
+  if (title) {
+    grl_media_set_title (media, title);
+  }
+
+  if (url) {
+    // Replace URL's daap:// with http://.
+    url[0] = 'h'; url[1] = 't'; url[2] = 't'; url[3] = 'p';
+    grl_media_set_url (media, url);
+  }
+
+  g_free (id_s);
+
+  cb->callback (cb->source,
+                cb->op_id,
+                media,
+                --cb->remaining,
+                cb->user_data,
+                NULL);
+}
+
+static void
+add_to_hash_table (gpointer key, gpointer value, GHashTable *hash_table)
+{
+  g_hash_table_insert (hash_table, key, value);
+}
+
+static void
+add_filtered_media_from_service (ResultCbAndArgsAndDb *cb_and_db)
+{
+  GHashTable *hash_table;
+  hash_table = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+  simple_dmap_db_filtered_foreach (cb_and_db->db,
+                                   cb_and_db->cb.skip,
+                                   cb_and_db->cb.count,
+                                   (GHRFunc) cb_and_db->cb.predicate,
+                                   cb_and_db->cb.predicate_data,
+                                   (GHFunc) add_to_hash_table,
+                                   hash_table);
+
+  cb_and_db->cb.remaining = g_hash_table_size (hash_table);
+  g_hash_table_foreach (hash_table, (GHFunc) add_media_from_service, &cb_and_db->cb);
+  g_hash_table_destroy (hash_table);
+  g_free (cb_and_db);
+}
+
+static void
+connected_cb (DMAPConnection       *connection,
+              gboolean              result,
+              const char           *reason,
+              ResultCbAndArgsAndDb *cb_and_db)
+{
+  // NOTE: connection argument is required by API but ignored in this case.
+  add_filtered_media_from_service (cb_and_db);
+}
+
+static void
+service_added_cb (DMAPMdnsBrowser *browser,
+                  DMAPMdnsBrowserService *service,
+                  GrlPlugin *plugin)
+{
+  GrlRegistry   *registry = grl_registry_get_default ();
+  GrlDmapSource *source   = grl_dmap_source_new (service);
+
+  GRL_DEBUG (__FUNCTION__);
+
+  grl_registry_register_source (registry,
+                                plugin,
+                                GRL_SOURCE (source),
+                                NULL);
+
+  g_hash_table_insert (sources, g_strdup (service->name), g_object_ref (source));
+}
+
+static void
+service_removed_cb (DMAPMdnsBrowser *browser,
+                    const gchar *service_name,
+                    GrlPlugin *plugin)
+{
+  GrlRegistry   *registry = grl_registry_get_default ();
+  GrlDmapSource *source   = g_hash_table_lookup (sources, service_name);
+
+  GRL_DEBUG (__FUNCTION__);
+
+  if (source) {
+    grl_registry_unregister_source (registry, GRL_SOURCE (source), NULL);
+    g_hash_table_remove (sources, service_name);
+  }
+}
+
+static void
+grl_dmap_connect (gchar *name, gchar *host, guint port, ResultCbAndArgsAndDb *cb_and_db, DMAPConnectionCallback callback)
+{
+  DMAPRecordFactory *factory;
+  DMAPConnection *connection;
+
+  factory = DMAP_RECORD_FACTORY (simple_daap_record_factory_new ());
+  connection = DMAP_CONNECTION (daap_connection_new (name, host, port, DMAP_DB (cb_and_db->db), factory));
+  dmap_connection_connect (connection, (DMAPConnectionCallback) callback, cb_and_db);
+}
+
+static gboolean
+always_true (gpointer key, gpointer value, gpointer user_data)
+{
+  return TRUE;
+}
+
+static gboolean
+match (gpointer key, DAAPRecord *record, gpointer user_data)
+{
+  char *title;
+  g_object_get (record, "title", &title, NULL);
+  return strstr (title, user_data) != NULL;
+}
+
+/* ================== API Implementation ================ */
+
+static const GList *
+grl_dmap_source_supported_keys (GrlSource *source)
+{
+  static GList *keys = NULL;
+
+  GRL_DEBUG (__func__);
+
+  if (!keys) {
+    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
+                                      GRL_METADATA_KEY_DURATION,
+                                      GRL_METADATA_KEY_TITLE,
+                                      GRL_METADATA_KEY_URL,
+                                      NULL);
+  }
+  return keys;
+}
+
+static void
+grl_dmap_source_browse (GrlSource *source,
+                        GrlSourceBrowseSpec *bs)
+{
+  GrlDmapSource *dmap_source = GRL_DMAP_SOURCE (source);
+  gchar *url = build_url (dmap_source->priv->service);
+
+  GRL_DEBUG (__func__);
+
+  ResultCbAndArgsAndDb *cb_and_db;
+
+  cb_and_db = g_new (ResultCbAndArgsAndDb, 1);
+
+  cb_and_db->cb.callback       = bs->callback;
+  cb_and_db->cb.source         = bs->source;
+  cb_and_db->cb.op_id          = bs->operation_id;
+  cb_and_db->cb.predicate      = always_true;
+  cb_and_db->cb.predicate_data = NULL;
+  cb_and_db->cb.skip           = grl_operation_options_get_skip (bs->options);
+  cb_and_db->cb.count          = grl_operation_options_get_count (bs->options);
+  cb_and_db->cb.user_data      = bs->user_data;
+
+  if ((cb_and_db->db = g_hash_table_lookup (connections, url))) {
+    // Just call directly; already connected, already populated database.
+    connected_cb (NULL, TRUE, NULL, cb_and_db);
+  } else {
+    // Connect.
+    cb_and_db->db = simple_dmap_db_new ();
+
+    grl_dmap_connect (dmap_source->priv->service->name,
+                      dmap_source->priv->service->host,
+                      dmap_source->priv->service->port,
+                      cb_and_db,
+                      (DMAPConnectionCallback) connected_cb);
+
+    g_hash_table_insert (connections, (gpointer) url, cb_and_db->db);
+  }
+
+  g_free (url);
+}
+
+static void grl_dmap_source_search (GrlSource *source,
+                                    GrlSourceSearchSpec *ss)
+{
+  GrlDmapSource *dmap_source = GRL_DMAP_SOURCE (source);
+
+  ResultCbAndArgsAndDb *cb_and_db;
+  DMAPMdnsBrowserService *service = dmap_source->priv->service;
+  gchar *url = build_url (service);
+
+  cb_and_db = g_new (ResultCbAndArgsAndDb, 1);
+
+  cb_and_db->cb.callback       = ss->callback;
+  cb_and_db->cb.source         = ss->source;
+  cb_and_db->cb.op_id          = ss->operation_id;
+  cb_and_db->cb.predicate      = (GHRFunc) match;
+  cb_and_db->cb.predicate_data = ss->text;
+  cb_and_db->cb.skip           = grl_operation_options_get_skip (ss->options);
+  cb_and_db->cb.count          = grl_operation_options_get_count (ss->options);
+  cb_and_db->cb.user_data      = ss->user_data;
+
+  if ((cb_and_db->db = g_hash_table_lookup (connections, url))) {
+    // Just call directly; already connected, already populated database.
+    connected_cb (NULL, TRUE, NULL, cb_and_db);
+  } else {
+    // Connect.
+    cb_and_db->db = simple_dmap_db_new ();
+    grl_dmap_connect (service->name, service->host, service->port, cb_and_db, (DMAPConnectionCallback) connected_cb);
+    g_hash_table_insert (connections, url, cb_and_db->db);
+  }
+
+  g_free (url);
+}
diff --git a/src/dmap/grl-dmap.h b/src/dmap/grl-dmap.h
new file mode 100644
index 0000000..cd9f820
--- /dev/null
+++ b/src/dmap/grl-dmap.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 W. Michael Petullo
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * Contact: W. Michael Petullo <mike flyn org>
+ *
+ * This library 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; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_DMAP_SOURCE_H_
+#define _GRL_DMAP_SOURCE_H_
+
+#include <grilo.h>
+
+#define GRL_DMAP_SOURCE_TYPE                    \
+  (grl_dmap_source_get_type ())
+
+#define GRL_DMAP_SOURCE(obj)                          \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                 \
+                               GRL_DMAP_SOURCE_TYPE,  \
+                               GrlDmapSource))
+
+#define GRL_IS_DMAP_SOURCE(obj)                       \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                 \
+                               GRL_DMAP_SOURCE_TYPE))
+
+#define GRL_DMAP_SOURCE_CLASS(klass)               \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                \
+                           GRL_DMAP_SOURCE_TYPE,   \
+                           GrlDmapSourceClass))
+
+#define GRL_IS_DMAP_SOURCE_CLASS(klass)            \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),                \
+                           GRL_DMAP_SOURCE_TYPE))
+
+#define GRL_UPNP_SOURCE_GET_CLASS(obj)                \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                  \
+                              GRL_DMAP_SOURCE_TYPE,   \
+                              GrlDmapSourceClass))
+
+typedef struct _GrlDmapSourcePrivate GrlDmapSourcePrivate;
+typedef struct _GrlDmapSource  GrlDmapSource;
+
+struct _GrlDmapSource {
+
+  GrlSource parent;
+
+  /*< private >*/
+  GrlDmapSourcePrivate *priv;
+};
+
+typedef struct _GrlDmapSourceClass GrlDmapSourceClass;
+
+struct _GrlDmapSourceClass {
+
+  GrlSourceClass parent_class;
+
+};
+
+GType grl_dmap_source_get_type (void);
+
+#endif /* _GRL_DMAP_SOURCE_H_ */
diff --git a/src/dmap/grl-dmap.xml b/src/dmap/grl-dmap.xml
new file mode 100644
index 0000000..61bebb1
--- /dev/null
+++ b/src/dmap/grl-dmap.xml
@@ -0,0 +1,10 @@
+<plugin>
+  <info>
+    <name>DMAP</name>
+    <module>libgrldmap</module>
+    <description>A plugin for browsing DMAP servers</description>
+    <author>W. Michael Petullo</author>
+    <license>LGPL</license>
+    <site>http://www.flyn.org</site>
+  </info>
+</plugin>
diff --git a/src/dmap/simple-daap-record-factory.c b/src/dmap/simple-daap-record-factory.c
new file mode 100644
index 0000000..7f4ac6d
--- /dev/null
+++ b/src/dmap/simple-daap-record-factory.c
@@ -0,0 +1,58 @@
+/*
+ * DAAPRecord factory class
+ *
+ * Copyright (C) 2008 W. Michael Petullo <mike flyn org>
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "simple-daap-record-factory.h"
+#include "simple-daap-record.h"
+
+DMAPRecord *
+simple_daap_record_factory_create  (DMAPRecordFactory *factory, gpointer user_data)
+{
+	return DMAP_RECORD (simple_daap_record_new ());
+}
+
+static void
+simple_daap_record_factory_init (SimpleDAAPRecordFactory *factory)
+{
+}
+
+static void
+simple_daap_record_factory_class_init (SimpleDAAPRecordFactoryClass *klass)
+{
+}
+
+static void
+simple_daap_record_factory_interface_init (gpointer iface, gpointer data)
+{
+	DMAPRecordFactoryIface *factory = iface;
+
+	g_assert (G_TYPE_FROM_INTERFACE (factory) == DMAP_TYPE_RECORD_FACTORY);
+
+	factory->create = simple_daap_record_factory_create;
+}
+
+G_DEFINE_TYPE_WITH_CODE (SimpleDAAPRecordFactory, simple_daap_record_factory, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (DMAP_TYPE_RECORD_FACTORY,
+					        simple_daap_record_factory_interface_init))
+
+SimpleDAAPRecordFactory *
+simple_daap_record_factory_new (void)
+{
+	return SIMPLE_DAAP_RECORD_FACTORY (g_object_new (TYPE_SIMPLE_DAAP_RECORD_FACTORY, NULL));
+}
diff --git a/src/dmap/simple-daap-record-factory.h b/src/dmap/simple-daap-record-factory.h
new file mode 100644
index 0000000..c94e1b5
--- /dev/null
+++ b/src/dmap/simple-daap-record-factory.h
@@ -0,0 +1,72 @@
+/*
+ * SimpleDAAPRecord factory class
+ *
+ * Copyright (C) 2008 W. Michael Petullo <mike flyn org>
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SIMPLE_DAAP_RECORD_FACTORY
+#define __SIMPLE_DAAP_RECORD_FACTORY
+
+#include <libdmapsharing/dmap.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_SIMPLE_DAAP_RECORD_FACTORY         \
+  (simple_daap_record_factory_get_type ())
+
+#define SIMPLE_DAAP_RECORD_FACTORY(o)                          \
+  (G_TYPE_CHECK_INSTANCE_CAST((o),                             \
+                              TYPE_SIMPLE_DAAP_RECORD_FACTORY, \
+                              SimpleDAAPRecordFactory))
+
+#define SIMPLE_DAAP_RECORD_FACTORY_CLASS(k)                 \
+  (G_TYPE_CHECK_CLASS_CAST((k),                             \
+                           TYPE_SIMPLE_DAAP_RECORD_FACTORY, \
+                           SimpleDAAPRecordFactoryClass))
+
+#define IS_SIMPLE_DAAP_RECORD_FACTORY(o)                          \
+  (G_TYPE_CHECK_INSTANCE_TYPE((o),                                \
+                              TYPE_SIMPLE_DAAP_RECORD_FACTORY))
+
+#define IS_SIMPLE_DAAP_RECORD_FACTORY_CLASS(k)                       \
+  (G_TYPE_CHECK_CLASS_TYPE((k),                                      \
+                           TYPE_SIMPLE_DAAP_RECORD_FACTORY_CLASS))
+
+#define SIMPLE_DAAP_RECORD_FACTORY_GET_CLASS(o)                \
+  (G_TYPE_INSTANCE_GET_CLASS((o),                              \
+                             TYPE_SIMPLE_DAAP_RECORD_FACTORY,  \
+                             SimpleDAAPRecordFactoryClass))
+
+typedef struct SimpleDAAPRecordFactoryPrivate SimpleDAAPRecordFactoryPrivate;
+
+typedef struct {
+  GObject parent;
+} SimpleDAAPRecordFactory;
+
+typedef struct {
+  GObjectClass parent;
+} SimpleDAAPRecordFactoryClass;
+
+GType                    simple_daap_record_factory_get_type (void);
+
+SimpleDAAPRecordFactory *simple_daap_record_factory_new      (void);
+
+DMAPRecord              *simple_daap_record_factory_create   (DMAPRecordFactory *factory, gpointer user_data);
+
+#endif /* __SIMPLE_DAAP_RECORD_FACTORY */
+
+G_END_DECLS
diff --git a/src/dmap/simple-daap-record.c b/src/dmap/simple-daap-record.c
new file mode 100644
index 0000000..dfcb37d
--- /dev/null
+++ b/src/dmap/simple-daap-record.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2012 W. Michael Petullo.
+ *
+ * Contact: W. Michael Petullo <mike flyn org>
+ *
+ * This library 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; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "simple-daap-record.h"
+
+struct SimpleDAAPRecordPrivate {
+  guint64 filesize;
+  char *location;
+  char *format;
+  gint mediakind;
+  char *title;
+  char *album;
+  char *sort_album;
+  char *artist;
+  char *sort_artist;
+  char *genre;
+  gboolean has_video;
+  gint rating;
+  gint32 duration;
+  gint32 track;
+  gint32 year;
+  gint32 firstseen;
+  gint32 mtime;
+  gint32 disc;
+  gint32 bitrate;
+};
+
+enum {
+  PROP_0,
+  PROP_LOCATION,
+  PROP_TITLE,
+  PROP_RATING,
+  PROP_FILESIZE,
+  PROP_ALBUM,
+  PROP_SORT_ALBUM,
+  PROP_ARTIST,
+  PROP_SORT_ARTIST,
+  PROP_GENRE,
+  PROP_FORMAT,
+  PROP_MEDIAKIND,
+  PROP_DURATION,
+  PROP_TRACK,
+  PROP_YEAR,
+  PROP_FIRSTSEEN,
+  PROP_MTIME,
+  PROP_DISC,
+  PROP_BITRATE,
+  PROP_HAS_VIDEO
+};
+
+static void
+simple_daap_record_set_property (GObject *object,
+                                 guint prop_id,
+                                 const GValue *value,
+                                 GParamSpec *pspec)
+{
+  SimpleDAAPRecord *record = SIMPLE_DAAP_RECORD (object);
+
+  switch (prop_id) {
+  case PROP_LOCATION:
+    g_free (record->priv->location);
+    record->priv->location = g_value_dup_string (value);
+    break;
+  case PROP_TITLE:
+    g_free (record->priv->title);
+    record->priv->title = g_value_dup_string (value);
+    break;
+  case PROP_ALBUM:
+    g_free (record->priv->album);
+    record->priv->album = g_value_dup_string(value);
+    break;
+  case PROP_SORT_ALBUM:
+    g_free (record->priv->sort_album);
+    record->priv->sort_album = g_value_dup_string(value);
+    break;
+  case PROP_ARTIST:
+    g_free (record->priv->artist);
+    record->priv->artist = g_value_dup_string(value);
+    break;
+  case PROP_SORT_ARTIST:
+    g_free (record->priv->sort_artist);
+    record->priv->sort_artist = g_value_dup_string(value);
+    break;
+  case PROP_GENRE:
+    g_free (record->priv->genre);
+    record->priv->genre = g_value_dup_string(value);
+    break;
+  case PROP_FORMAT:
+    g_free (record->priv->format);
+    record->priv->format = g_value_dup_string(value);
+    break;
+  case PROP_MEDIAKIND:
+    record->priv->mediakind = g_value_get_enum (value);
+    break;
+  case PROP_RATING:
+    record->priv->rating = g_value_get_int (value);
+    break;
+  case PROP_FILESIZE:
+    record->priv->filesize = g_value_get_uint64 (value);
+    break;
+  case PROP_DURATION:
+    record->priv->duration = g_value_get_int (value);
+    break;
+  case PROP_TRACK:
+    record->priv->track = g_value_get_int (value);
+    break;
+  case PROP_YEAR:
+    record->priv->year = g_value_get_int (value);
+    break;
+  case PROP_FIRSTSEEN:
+    record->priv->firstseen = g_value_get_int (value);
+    break;
+  case PROP_MTIME:
+    record->priv->mtime = g_value_get_int (value);
+    break;
+  case PROP_DISC:
+    record->priv->disc = g_value_get_int (value);
+    break;
+  case PROP_BITRATE:
+    record->priv->bitrate = g_value_get_int (value);
+    break;
+  case PROP_HAS_VIDEO:
+    record->priv->has_video = g_value_get_boolean (value);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
+                                       prop_id,
+                                       pspec);
+    break;
+  }
+}
+
+static void
+simple_daap_record_get_property (GObject *object,
+                                 guint prop_id,
+                                 GValue *value,
+                                 GParamSpec *pspec)
+{
+  SimpleDAAPRecord *record = SIMPLE_DAAP_RECORD (object);
+
+  switch (prop_id) {
+  case PROP_LOCATION:
+    g_value_set_static_string (value, record->priv->location);
+    break;
+  case PROP_TITLE:
+    g_value_set_static_string (value, record->priv->title);
+    break;
+  case PROP_ALBUM:
+    g_value_set_static_string (value, record->priv->album);
+    break;
+  case PROP_SORT_ALBUM:
+    g_value_set_static_string (value, record->priv->sort_album);
+    break;
+  case PROP_ARTIST:
+    g_value_set_static_string (value, record->priv->artist);
+    break;
+  case PROP_SORT_ARTIST:
+    g_value_set_static_string (value, record->priv->sort_artist);
+    break;
+  case PROP_GENRE:
+    g_value_set_static_string (value, record->priv->genre);
+    break;
+  case PROP_FORMAT:
+    g_value_set_static_string (value, record->priv->format);
+    break;
+  case PROP_MEDIAKIND:
+    g_value_set_enum (value, record->priv->mediakind);
+    break;
+  case PROP_RATING:
+    g_value_set_int (value, record->priv->rating);
+    break;
+  case PROP_FILESIZE:
+    g_value_set_uint64 (value, record->priv->filesize);
+    break;
+  case PROP_DURATION:
+    g_value_set_int (value, record->priv->duration);
+    break;
+  case PROP_TRACK:
+    g_value_set_int (value, record->priv->track);
+    break;
+  case PROP_YEAR:
+    g_value_set_int (value, record->priv->year);
+    break;
+  case PROP_FIRSTSEEN:
+    g_value_set_int (value, record->priv->firstseen);
+    break;
+  case PROP_MTIME:
+    g_value_set_int (value, record->priv->mtime);
+    break;
+  case PROP_DISC:
+    g_value_set_int (value, record->priv->disc);
+    break;
+  case PROP_BITRATE:
+    g_value_set_int (value, record->priv->bitrate);
+    break;
+  case PROP_HAS_VIDEO:
+    g_value_set_boolean (value, record->priv->has_video);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
+                                       prop_id,
+                                       pspec);
+    break;
+  }
+}
+
+SimpleDAAPRecord *
+simple_daap_record_new (void)
+{
+  return SIMPLE_DAAP_RECORD (g_object_new (TYPE_SIMPLE_DAAP_RECORD, NULL));
+}
+
+GInputStream *
+simple_daap_record_read (DAAPRecord *record, GError **error)
+{
+  GFile *file;
+  GInputStream *stream;
+
+  file = g_file_new_for_uri (SIMPLE_DAAP_RECORD (record)->priv->location);
+  stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+  g_object_unref (file);
+
+  return stream;
+}
+
+static void
+simple_daap_record_init (SimpleDAAPRecord *record)
+{
+  record->priv = SIMPLE_DAAP_RECORD_GET_PRIVATE (record);
+}
+
+static void simple_daap_record_finalize (GObject *object);
+
+static void
+simple_daap_record_class_init (SimpleDAAPRecordClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (SimpleDAAPRecordPrivate));
+
+  gobject_class->set_property = simple_daap_record_set_property;
+  gobject_class->get_property = simple_daap_record_get_property;
+  gobject_class->finalize     = simple_daap_record_finalize;
+
+  g_object_class_override_property (gobject_class, PROP_LOCATION, "location");
+  g_object_class_override_property (gobject_class, PROP_TITLE, "title");
+  g_object_class_override_property (gobject_class, PROP_ALBUM, "songalbum");
+  g_object_class_override_property (gobject_class, PROP_SORT_ALBUM, "sort-album");
+  g_object_class_override_property (gobject_class, PROP_ARTIST, "songartist");
+  g_object_class_override_property (gobject_class, PROP_SORT_ARTIST, "sort-artist");
+  g_object_class_override_property (gobject_class, PROP_GENRE, "songgenre");
+  g_object_class_override_property (gobject_class, PROP_FORMAT, "format");
+  g_object_class_override_property (gobject_class, PROP_RATING, "rating");
+  g_object_class_override_property (gobject_class, PROP_FILESIZE, "filesize");
+  g_object_class_override_property (gobject_class, PROP_DURATION, "duration");
+  g_object_class_override_property (gobject_class, PROP_TRACK, "track");
+  g_object_class_override_property (gobject_class, PROP_YEAR, "year");
+  g_object_class_override_property (gobject_class, PROP_FIRSTSEEN, "firstseen");
+  g_object_class_override_property (gobject_class, PROP_MTIME, "mtime");
+  g_object_class_override_property (gobject_class, PROP_DISC, "disc");
+  g_object_class_override_property (gobject_class, PROP_BITRATE, "bitrate");
+  g_object_class_override_property (gobject_class, PROP_HAS_VIDEO, "has-video");
+  g_object_class_override_property (gobject_class, PROP_MEDIAKIND, "mediakind");
+}
+
+static void
+simple_daap_record_daap_iface_init (gpointer iface, gpointer data)
+{
+  DAAPRecordIface *daap_record = iface;
+
+  g_assert (G_TYPE_FROM_INTERFACE (daap_record) == DAAP_TYPE_RECORD);
+
+  daap_record->read = simple_daap_record_read;
+}
+
+static void
+simple_daap_record_dmap_iface_init (gpointer iface, gpointer data)
+{
+  DMAPRecordIface *dmap_record = iface;
+
+  g_assert (G_TYPE_FROM_INTERFACE (dmap_record) == DMAP_TYPE_RECORD);
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (SimpleDAAPRecord, simple_daap_record, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (DAAP_TYPE_RECORD, simple_daap_record_daap_iface_init)
+                         G_IMPLEMENT_INTERFACE (DMAP_TYPE_RECORD, simple_daap_record_dmap_iface_init))
+
+static void
+simple_daap_record_finalize (GObject *object)
+{
+  SimpleDAAPRecord *record = SIMPLE_DAAP_RECORD (object);
+
+  g_debug ("Free'ing record");
+
+  g_free (record->priv->location);
+  g_free (record->priv->title);
+  g_free (record->priv->album);
+  g_free (record->priv->sort_album);
+  g_free (record->priv->artist);
+  g_free (record->priv->sort_artist);
+  g_free (record->priv->genre);
+  g_free (record->priv->format);
+
+  G_OBJECT_CLASS (simple_daap_record_parent_class)->finalize (object);
+}
diff --git a/src/dmap/simple-daap-record.h b/src/dmap/simple-daap-record.h
new file mode 100644
index 0000000..fb4735b
--- /dev/null
+++ b/src/dmap/simple-daap-record.h
@@ -0,0 +1,78 @@
+/*
+ *  Database record class for DAAP sharing
+ *
+ *  Copyright (C) 2008 W. Michael Petullo <mike flyn org>
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SIMPLE_DAAP_RECORD
+#define __SIMPLE_DAAP_RECORD
+
+#include <libdmapsharing/dmap.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_SIMPLE_DAAP_RECORD                 \
+  (simple_daap_record_get_type ())
+
+#define SIMPLE_DAAP_RECORD(o)                            \
+  (G_TYPE_CHECK_INSTANCE_CAST((o),                       \
+                              TYPE_SIMPLE_DAAP_RECORD,   \
+                              SimpleDAAPRecord))
+
+#define SIMPLE_DAAP_RECORD_CLASS(k)                   \
+  (G_TYPE_CHECK_CLASS_CAST((k),                       \
+                           TYPE_SIMPLE_DAAP_RECORD,   \
+                           SimpleDAAPRecordClass))
+
+#define IS_SIMPLE_DAAP_RECORD(o)                         \
+  (G_TYPE_CHECK_INSTANCE_TYPE((o),                       \
+                              TYPE_SIMPLE_DAAP_RECORD))
+
+#define IS_SIMPLE_DAAP_RECORD_CLASS(k)                      \
+  (G_TYPE_CHECK_CLASS_TYPE((k),                             \
+                           TYPE_SIMPLE_DAAP_RECORD_CLASS))
+
+#define SIMPLE_DAAP_RECORD_GET_CLASS(o)               \
+  (G_TYPE_INSTANCE_GET_CLASS((o),                     \
+                             TYPE_SIMPLE_DAAP_RECORD, \
+                             SimpleDAAPRecordClass))
+
+#define SIMPLE_DAAP_RECORD_GET_PRIVATE(o)                \
+  (G_TYPE_INSTANCE_GET_PRIVATE((o),                      \
+                               TYPE_SIMPLE_DAAP_RECORD,  \
+                               SimpleDAAPRecordPrivate))
+
+typedef struct SimpleDAAPRecordPrivate SimpleDAAPRecordPrivate;
+
+typedef struct {
+  GObject parent;
+  SimpleDAAPRecordPrivate *priv;
+} SimpleDAAPRecord;
+
+typedef struct {
+  GObjectClass parent;
+} SimpleDAAPRecordClass;
+
+GType simple_daap_record_get_type (void);
+
+SimpleDAAPRecord *simple_daap_record_new    (void);
+GInputStream     *simple_daap_record_read   (DAAPRecord *record, GError **error);
+gint              simple_daap_record_get_id (DAAPRecord *record);
+
+#endif /* __SIMPLE_DAAP_RECORD */
+
+G_END_DECLS
diff --git a/src/dmap/simple-dmap-db.c b/src/dmap/simple-dmap-db.c
new file mode 100644
index 0000000..f49ee6a
--- /dev/null
+++ b/src/dmap/simple-dmap-db.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 W. Michael Petullo.
+ *
+ * Contact: W. Michael Petullo <mike flyn org>
+ *
+ * This library 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; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <glib.h>
+
+#include "simple-dmap-db.h"
+
+/* Media ID's start at max and go down. Container ID's start at 1 and go up. */
+static guint nextid = G_MAXINT; /* NOTE: this should be G_MAXUINT, but iPhoto can't handle it. */
+
+struct SimpleDMAPDbPrivate {
+  GHashTable *db;
+};
+
+enum {
+  PROP_0,
+  PROP_RECORD_FACTORY,
+};
+
+SimpleDMAPDb *
+simple_dmap_db_new (void)
+{
+  return SIMPLE_DMAP_DB (g_object_new (TYPE_SIMPLE_DMAP_DB, NULL));
+}
+
+static DMAPRecord *
+simple_dmap_db_lookup_by_id (const DMAPDb *db, guint id)
+{
+  DMAPRecord *record;
+
+  record = g_hash_table_lookup (SIMPLE_DMAP_DB (db)->priv->db, GUINT_TO_POINTER (id));
+  g_object_ref (record);
+
+  return record;
+}
+
+static void
+simple_dmap_db_foreach (const DMAPDb *db,
+                        GHFunc func,
+                        gpointer data)
+{
+  g_hash_table_foreach (SIMPLE_DMAP_DB (db)->priv->db, func, data);
+}
+
+static gint64
+simple_dmap_db_count (const DMAPDb *db)
+{
+  return g_hash_table_size (SIMPLE_DMAP_DB (db)->priv->db);
+}
+
+static guint
+simple_dmap_db_add (DMAPDb *db, DMAPRecord *record)
+{
+  g_object_ref (record);
+  g_hash_table_insert (SIMPLE_DMAP_DB (db)->priv->db, GUINT_TO_POINTER (nextid--), record);
+
+  return nextid;
+}
+
+static void
+simple_dmap_db_interface_init (gpointer iface, gpointer data)
+{
+  DMAPDbIface *dmap_db = iface;
+
+  g_assert (G_TYPE_FROM_INTERFACE (dmap_db) == DMAP_TYPE_DB);
+
+  dmap_db->add = simple_dmap_db_add;
+  dmap_db->lookup_by_id = simple_dmap_db_lookup_by_id;
+  dmap_db->foreach = simple_dmap_db_foreach;
+  dmap_db->count = simple_dmap_db_count;
+}
+
+void
+simple_dmap_db_filtered_foreach (SimpleDMAPDb *db,
+                                 guint skip,
+                                 guint count,
+                                 GHRFunc predicate,
+                                 gpointer pred_user_data,
+                                 GHFunc func,
+                                 gpointer user_data)
+{
+  GHashTableIter iter;
+  gpointer key, val;
+  guint i;
+
+  g_hash_table_iter_init (&iter, db->priv->db);
+  for (i = 0; g_hash_table_iter_next (&iter, &key, &val); i++) {
+    if (i < skip) {
+      continue;
+    }
+    if (i == skip + count) {
+      break;
+    }
+    if (predicate (key, val, pred_user_data)) {
+      func (key, val, user_data);
+    }
+  }
+}
+
+G_DEFINE_TYPE_WITH_CODE (SimpleDMAPDb, simple_dmap_db, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (DMAP_TYPE_DB, simple_dmap_db_interface_init))
+
+static GObject*
+simple_dmap_db_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params)
+{
+  GObject *object;
+
+  object = G_OBJECT_CLASS (simple_dmap_db_parent_class)->constructor (type, n_construct_params, construct_params);
+
+  return object;
+}
+
+static void simple_dmap_db_init (SimpleDMAPDb *db)
+{
+  db->priv = SIMPLE_DMAP_DB_GET_PRIVATE (db);
+  db->priv->db = g_hash_table_new_full (g_direct_hash,
+                                        g_direct_equal,
+                                        NULL,
+                                        g_object_unref);
+}
+
+static void
+simple_dmap_db_finalize (GObject *object)
+{
+  SimpleDMAPDb *db = SIMPLE_DMAP_DB (object);
+
+  g_debug ("Finalizing SimpleDMAPDb (%d records)",
+           g_hash_table_size (db->priv->db));
+
+  g_hash_table_destroy (db->priv->db);
+}
+
+static void
+simple_dmap_db_set_property (GObject *object,
+                             guint prop_id,
+                             const GValue *value,
+                             GParamSpec *pspec)
+{
+  switch (prop_id) {
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+simple_dmap_db_get_property (GObject *object,
+                             guint prop_id,
+                             GValue *value,
+                             GParamSpec *pspec)
+{
+  switch (prop_id) {
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    break;
+  }
+}
+
+
+static void simple_dmap_db_class_init (SimpleDMAPDbClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (SimpleDMAPDbPrivate));
+
+  object_class->finalize = simple_dmap_db_finalize;
+  object_class->constructor = simple_dmap_db_constructor;
+  object_class->set_property = simple_dmap_db_set_property;
+  object_class->get_property = simple_dmap_db_get_property;
+}
diff --git a/src/dmap/simple-dmap-db.h b/src/dmap/simple-dmap-db.h
new file mode 100644
index 0000000..f0e5ae5
--- /dev/null
+++ b/src/dmap/simple-dmap-db.h
@@ -0,0 +1,82 @@
+/*
+ *  Database class for DMAP sharing
+ *
+ *  Copyright (C) 2008 W. Michael Petullo <mike flyn org>
+ *
+ * This library 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.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SIMPLE_DMAP_DB
+#define __SIMPLE_DMAP_DB
+
+#include <libdmapsharing/dmap.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_SIMPLE_DMAP_DB                     \
+  (simple_dmap_db_get_type ())
+
+#define SIMPLE_DMAP_DB(o)                             \
+  (G_TYPE_CHECK_INSTANCE_CAST ((o),                   \
+                               TYPE_SIMPLE_DMAP_DB,   \
+                               SimpleDMAPDb))
+
+#define SIMPLE_DMAP_DB_CLASS(k)                 \
+  (G_TYPE_CHECK_CLASS_CAST((k),                 \
+                           TYPE_SIMPLE_DMAP_DB, \
+                           SimpleDMAPDbClass))
+#define IS_SIMPLE_DMAP_DB(o)                          \
+  (G_TYPE_CHECK_INSTANCE_TYPE((o),                    \
+                              TYPE_SIMPLE_DMAP_DB))
+#define IS_SIMPLE_DMAP_DB_CLASS(k)                       \
+  (G_TYPE_CHECK_CLASS_TYPE((k),                          \
+                           TYPE_SIMPLE_DMAP_DB_CLASS))
+
+#define SIMPLE_DMAP_DB_GET_CLASS(o)                \
+  (G_TYPE_INSTANCE_GET_CLASS((o),                  \
+                             TYPE_SIMPLE_DMAP_DB,  \
+                             SimpleDMAPDbClass))
+
+#define SIMPLE_DMAP_DB_GET_PRIVATE(o)                 \
+  (G_TYPE_INSTANCE_GET_PRIVATE((o),                   \
+                               TYPE_SIMPLE_DMAP_DB,   \
+                               SimpleDMAPDbPrivate))
+
+typedef struct SimpleDMAPDbPrivate SimpleDMAPDbPrivate;
+
+typedef struct {
+  GObject parent;
+  SimpleDMAPDbPrivate *priv;
+} SimpleDMAPDb;
+
+typedef struct {
+  GObjectClass parent;
+} SimpleDMAPDbClass;
+
+void simple_dmap_db_filtered_foreach (SimpleDMAPDb *db,
+                                      guint skip,
+                                      guint count,
+                                      GHRFunc predicate,
+                                      gpointer pred_user_data,
+                                      GHFunc func,
+                                      gpointer user_data);
+
+SimpleDMAPDb *simple_dmap_db_new (void);
+
+GType simple_dmap_db_get_type (void);
+
+#endif /* __SIMPLE_DMAP_DB */
+
+G_END_DECLS



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