diff --git a/configure.ac b/configure.ac
index bcb9439..3772d5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,7 @@ AC_CONFIG_SRCDIR([src])
 AM_CONFIG_HEADER(config.h)
 
 AC_PROG_CC
+AM_PROG_VALAC
 AC_PROG_INSTALL
 
 PKG_CONFIG="pkg-config"
@@ -116,6 +117,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 gee-1.0, 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)
@@ -311,6 +314,43 @@ AC_SUBST(UPNP_PLUGIN_ID)
 AC_DEFINE_UNQUOTED([UPNP_PLUGIN_ID], ["$UPNP_PLUGIN_ID"], [UPnP 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])
+
+# ----------------------------------------------------------
 # BUILD YOUTUBE PLUGIN
 # ----------------------------------------------------------
 
@@ -762,6 +802,7 @@ AC_CONFIG_FILES([
   Makefile
   src/Makefile
   src/media/upnp/Makefile
+  src/media/dmap/Makefile
   src/media/vimeo/Makefile
   src/media/bookmarks/Makefile
   src/media/tracker/Makefile
diff --git a/src/media/Makefile.am b/src/media/Makefile.am
index df7df83..493ed52 100644
--- a/src/media/Makefile.am
+++ b/src/media/Makefile.am
@@ -20,6 +20,10 @@ if UPNP_PLUGIN
 SUBDIRS += upnp
 endif
 
+if DMAP_PLUGIN
+SUBDIRS += dmap
+endif
+
 if YOUTUBE_PLUGIN
 SUBDIRS += youtube
 endif
@@ -52,8 +56,8 @@ if TRACKER_PLUGIN
 SUBDIRS += tracker
 endif
 
-DIST_SUBDIRS =									\
-	filesystem jamendo upnp youtube flickr podcasts bookmarks shoutcast	\
+DIST_SUBDIRS =										\
+	filesystem jamendo upnp dmap youtube flickr podcasts bookmarks shoutcast	\
 	apple-trailers vimeo tracker
 
 MAINTAINERCLEANFILES =	\
diff --git a/src/media/dmap/Makefile.am b/src/media/dmap/Makefile.am
new file mode 100644
index 0000000..994e6cc
--- /dev/null
+++ b/src/media/dmap/Makefile.am
@@ -0,0 +1,59 @@
+#
+# Makefile.am
+#
+# Author: W. Michael Petullo <mike@flyn.org>
+#
+# Copyright (C) 2011 W. Michael Petullo. All rights reserved.
+
+lib_LTLIBRARIES	= libgrldmap.la
+
+libgrldmap_la_CFLAGS =		\
+	$(DEPS_CFLAGS)		\
+	$(DMAP_CFLAGS)		\
+	$(XML_CFLAGS)
+
+libgrldmap_la_LIBADD =	\
+	$(DEPS_LIBS)	\
+	$(DMAP_LIBS)	\
+	$(XML_LIBS)
+
+libgrldmap_la_LDFLAGS = \
+	-module		\
+	-avoid-version
+
+libgrldmap_la_CFLAGS +=	\
+	$(XML_CFLAGS)
+
+libgrldmap_la_LIBADD +=	\
+	$(XML_LIBS)
+
+libgrldmap_la_VALASOURCES =     \
+	simple-dmap-db.vala     \
+	simple-daap-record.vala
+
+libgrldmap_la_VALABUILTSOURCES = $(libgrldmap_la_VALASOURCES:.vala=.c) $(libgrldmap_la_VALASOURCES:.vala=.h)
+
+libgrldmap_la_SOURCES =                   \
+	$(libgrldmap_la_VALABUILTSOURCES) \
+	grl-dmap.c                        \
+	grl-dmap.h
+
+libdir		= $(GRL_PLUGINS_DIR)
+dmapxmldir	= $(GRL_PLUGINS_CONF_DIR)
+dmapxml_DATA	= $(DMAP_PLUGIN_ID).xml
+
+EXTRA_DIST =                              \
+	$(libgrldmap_la_VALABUILTSOURCES) \
+	$(dmapxml_DATA) 
+
+MAINTAINERCLEANFILES =                    \
+	$(libgrldmap_la_VALABUILTSOURCES) \
+	*.in                              \
+	*~
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
+
+.vala.c:
+	$(VALAC) -C -H $(basename $@).h --pkg gee-1.0 --pkg gstreamer-0.10 --pkg libdmapsharing-3.0 --pkg libsoup-2.4 --pkg glib-2.0 $^
+
+SUFFIXES=.vala .c
diff --git a/src/media/dmap/grl-dmap.c b/src/media/dmap/grl-dmap.c
new file mode 100644
index 0000000..a61d307
--- /dev/null
+++ b/src/media/dmap/grl-dmap.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2011 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
+ *
+ */
+
+#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"
+
+/* --------- 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   "grl-dmap"
+#define SOURCE_NAME "DMAP"
+#define SOURCE_DESC "A plugin for browsing DMAP servers"
+
+/* --- Grilo Filesystem Private --- */
+
+#define GRL_DMAP_SOURCE_GET_PRIVATE(object)         \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),            \
+			     GRL_DMAP_SOURCE_TYPE,  \
+			     GrlDmapSourcePrivate))
+
+struct _GrlDmapSourcePrivate {
+  DMAPMdnsBrowser *browser;
+  DMAPConnection *connection;
+  GList *services;
+  SimpleDMAPDb *db;
+  gboolean connected;
+};
+
+/* --- Data types --- */
+
+typedef struct _ResultCbAndArgs {
+  GrlMediaSourceResultCb callback;
+  GrlMediaSource *source;
+  guint op_id;
+  GHRFunc predicate;
+  gchar *predicate_data;
+  guint remaining;
+  gpointer user_data;
+} ResultCbAndArgs;
+
+typedef struct _ResultCbAndArgsAndDb {
+  ResultCbAndArgs cb;
+  SimpleDMAPDb *db;
+} ResultCbAndArgsAndDb;
+
+static GrlDmapSource *grl_dmap_source_new (void);
+
+static void grl_dmap_source_finalize (GObject *object);
+
+gboolean grl_dmap_plugin_init (GrlPluginRegistry *registry,
+                               const GrlPluginInfo *plugin,
+                               GList *configs);
+
+static const GList *grl_dmap_source_supported_keys (GrlMetadataSource *source);
+
+static void grl_dmap_source_browse (GrlMediaSource *source,
+                                    GrlMediaSourceBrowseSpec *bs);
+
+static void grl_dmap_source_search (GrlMediaSource *source,
+                                    GrlMediaSourceSearchSpec *ss);
+
+
+static void service_added_cb (DMAPMdnsBrowser *browser,
+                              DMAPMdnsBrowserService *service,
+                              GrlDmapSource *source);
+
+/* =================== Filesystem Plugin  =============== */
+
+gboolean
+grl_dmap_plugin_init (GrlPluginRegistry *registry,
+                      const GrlPluginInfo *plugin,
+                      GList *configs)
+{
+  GError *error = NULL;
+
+  GRL_LOG_DOMAIN_INIT (dmap_log_domain, "dmap");
+
+  GRL_DEBUG ("dmap_plugin_init");
+
+  GrlDmapSource *source = grl_dmap_source_new ();
+
+  grl_plugin_registry_register_source (registry,
+                                       plugin,
+                                       GRL_MEDIA_PLUGIN (source),
+                                       NULL);
+
+  source->priv->browser = dmap_mdns_browser_new (DMAP_MDNS_BROWSER_SERVICE_TYPE_DAAP);
+  source->priv->connection = NULL;
+  source->priv->services   = NULL;
+  source->priv->db         = NULL;
+
+  g_signal_connect (G_OBJECT (source->priv->browser),
+                   "service-added",
+                    G_CALLBACK (service_added_cb),
+                    source);
+
+  dmap_mdns_browser_start (source->priv->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);
+
+/* ================== Filesystem GObject ================ */
+
+G_DEFINE_TYPE (GrlDmapSource,
+               grl_dmap_source,
+               GRL_TYPE_MEDIA_SOURCE);
+
+static GrlDmapSource *
+grl_dmap_source_new (void)
+{
+  GRL_DEBUG ("grl_dmap_source_new");
+  return g_object_new (GRL_DMAP_SOURCE_TYPE,
+                      "source-id",   SOURCE_ID,
+		      "source-name", SOURCE_NAME,
+		      "source-desc", SOURCE_DESC,
+		      NULL);
+}
+
+static void
+grl_dmap_source_class_init (GrlDmapSourceClass * klass)
+{
+  GrlMediaSourceClass *source_class = GRL_MEDIA_SOURCE_CLASS (klass);
+  GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+  source_class->browse = grl_dmap_source_browse;
+  source_class->search = grl_dmap_source_search;
+  G_OBJECT_CLASS (source_class)->finalize = grl_dmap_source_finalize;
+  metadata_class->supported_keys = grl_dmap_source_supported_keys;
+  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 void
+add_media_box_for_service (DMAPMdnsBrowserService *service,
+                           GrlMediaSourceBrowseSpec *bs)
+{
+  GrlMedia *media = GRL_MEDIA (grl_media_box_new ());
+
+  gchar *url = g_strdup_printf ("%s://%s:%u",
+                                 service->name,
+				 service->host,
+				 service->port);
+
+  grl_media_set_id (media, service->name);
+  grl_media_set_title (media, service->name);
+  grl_media_set_url (media, url);
+
+  g_free (url);
+
+  bs->callback (bs->source,
+                bs->browse_id,
+                media,
+                0,
+                bs->user_data,
+                NULL);
+}
+
+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);
+  g_assert (title);
+  g_assert (url);
+
+  // Replace URL's daap:// with http://.
+  url[0] = 'h'; url[1] = 't'; url[2] = 't'; url[3] = 'p';
+
+  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_title    (media, title);
+  grl_media_set_url      (media, url);
+  grl_media_set_duration (media, duration);
+
+  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 (SimpleDMAPDb *db, GHRFunc predicate, 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, (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)
+{
+  add_filtered_media_from_service (cb_and_db->db, (GHRFunc) cb_and_db->cb.predicate, cb_and_db);
+  GRL_DMAP_SOURCE (cb_and_db->cb.source)->priv->connected = TRUE;
+}
+
+static void
+service_added_cb (DMAPMdnsBrowser *browser,
+                  DMAPMdnsBrowserService *service,
+                  GrlDmapSource *source)
+{
+  source->priv->db = simple_dmap_db_new ();
+  source->priv->services = g_list_append (source->priv->services, service);
+}
+
+/* ================== API Implementation ================ */
+
+static const GList *
+grl_dmap_source_supported_keys (GrlMetadataSource *source)
+{
+  static GList *keys = NULL;
+
+  GRL_DEBUG (__func__);
+
+  if (!keys) {
+    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
+                                      GRL_METADATA_KEY_TITLE,
+                                      GRL_METADATA_KEY_URL,
+                                      GRL_METADATA_KEY_MIME,
+                                      GRL_METADATA_KEY_DATE,
+                                      GRL_METADATA_KEY_DURATION,
+                                      GRL_METADATA_KEY_ARTIST,
+                                      GRL_METADATA_KEY_ALBUM,
+                                      GRL_METADATA_KEY_GENRE,
+                                      GRL_METADATA_KEY_CHILDCOUNT,
+                                      GRL_METADATA_KEY_THUMBNAIL,
+                                      NULL);
+  }
+  return keys;
+}
+
+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;
+}
+
+static void
+grl_dmap_source_browse (GrlMediaSource *source,
+                        GrlMediaSourceBrowseSpec *bs)
+{
+  const gchar *id, *url;
+  GrlDmapSource *dmap_source = GRL_DMAP_SOURCE (source);
+
+  GRL_DEBUG (__func__);
+
+  id  = grl_media_get_id  (bs->container); 
+  url = grl_media_get_url (bs->container); 
+
+  if (id == NULL) {
+    g_list_foreach (dmap_source->priv->services, (GFunc) add_media_box_for_service, bs);
+  } else {
+    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->browse_id;
+    cb_and_db->cb.predicate      = always_true;
+    cb_and_db->cb.predicate_data = NULL;
+    cb_and_db->cb.user_data      = bs->user_data;
+    cb_and_db->db                = dmap_source->priv->db;
+
+    if (TRUE == dmap_source->priv->connected) {
+      // Just call directly; already connected, already populated database.
+      connected_cb (dmap_source->priv->connection, TRUE, NULL, cb_and_db);
+    } else {
+      // Connect.
+      gchar *url_copy, *ptr, *name, *host;
+      guint port;
+
+      g_assert (url);
+
+      url_copy = g_strdup (url);
+      name = url_copy;
+      ptr = g_strstr_len (url_copy, -1, ":");
+      g_assert (ptr);
+      *ptr = 0x00;
+
+      host = ptr + 3; // Advance past "://".
+      ptr = g_strstr_len (host, -1, ":");
+      g_assert (ptr);
+      *ptr = 0x00;
+
+      g_assert (++ptr); // Advance past ":".
+      errno = 0;
+      port = strtol(ptr, NULL, 10);
+      g_assert (errno == 0);
+
+      grl_dmap_connect (name, host, port, cb_and_db, (DMAPConnectionCallback) connected_cb);
+    }
+  }
+}
+
+static void grl_dmap_source_search (GrlMediaSource *source,
+                                    GrlMediaSourceSearchSpec *ss)
+{
+  GList *list;
+  ResultCbAndArgsAndDb *cb_and_db;
+  GrlDmapSource *dmap_source = GRL_DMAP_SOURCE (source);
+
+  for (list = ss->keys; list; list = list->next) {
+    g_print ("%p\n", list->data);
+  }
+
+  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->search_id;
+  cb_and_db->cb.predicate      = (GHRFunc) match;
+  cb_and_db->cb.predicate_data = ss->text;
+  cb_and_db->cb.user_data      = ss->user_data;
+  cb_and_db->db                = dmap_source->priv->db;
+
+  if (TRUE == dmap_source->priv->connected) {
+    // Just call directly; already connected, already populated database.
+    connected_cb (dmap_source->priv->connection, TRUE, NULL, cb_and_db);
+  } else {
+    // Connect.
+    for (list = GRL_DMAP_SOURCE (ss->source)->priv->services; list; list = list->next) {
+      DMAPMdnsBrowserService *service = list->data;
+      grl_dmap_connect (service->name, service->host, service->port, cb_and_db, (DMAPConnectionCallback) connected_cb);
+    }
+  }
+}
diff --git a/src/media/dmap/grl-dmap.h b/src/media/dmap/grl-dmap.h
new file mode 100644
index 0000000..978a5b2
--- /dev/null
+++ b/src/media/dmap/grl-dmap.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 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
+ *
+ */
+
+#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 {
+
+  GrlMediaSource parent;
+
+  /*< private >*/
+  GrlDmapSourcePrivate *priv;
+};
+
+typedef struct _GrlDmapSourceClass GrlDmapSourceClass;
+
+struct _GrlDmapSourceClass {
+
+  GrlMediaSourceClass parent_class;
+
+};
+
+GType grl_dmap_source_get_type (void);
+
+#endif /* _GRL_DMAP_SOURCE_H_ */
diff --git a/src/media/dmap/grl-dmap.xml b/src/media/dmap/grl-dmap.xml
new file mode 100644
index 0000000..61bebb1
--- /dev/null
+++ b/src/media/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/media/dmap/simple-daap-record.vala b/src/media/dmap/simple-daap-record.vala
new file mode 100644
index 0000000..896afec
--- /dev/null
+++ b/src/media/dmap/simple-daap-record.vala
@@ -0,0 +1,42 @@
+// A DAAPRecord implementation.
+public class SimpleDAAPRecord : GLib.Object, DMAP.Record, DAAP.Record {
+	public string location { get; set; }
+	public string title { get; set; }
+	public int rating { get; set; }
+	public int filesize { get; set; }
+	public string album { get; set; }
+	public string artist { get; set; }
+	public string genre { get; set; }
+	public string format { get; set; }
+	public int duration { get; set; }
+	public int track { get; set; }
+	public int year { get; set; }
+	public int firstseen { get; set; }
+	public int mtime { get; set; }
+	public int disc { get; set; }
+	public ulong bitrate { get; set; }
+	public bool has_video { get; set; }
+
+	public unowned GLib.InputStream read () throws GLib.Error {
+		GLib.error ("read not implemented");
+	}
+
+	public unowned DMAP.Record set_from_blob (GLib.ByteArray blob) {
+		GLib.error ("set_from_blob not implemented");
+	}
+
+	public unowned GLib.ByteArray to_blob () {
+		GLib.error ("to_blob not implemented");
+	}
+
+	public bool itunes_compat () {
+		GLib.error ("itunes_compat not implemented");
+	}
+}
+
+// A DAAPRecordFactory implementation.
+public class SimpleDAAPRecordFactory : GLib.Object, DMAP.RecordFactory {
+	public DMAP.Record create (void* user_data) {
+		return new SimpleDAAPRecord ();
+	}
+}
diff --git a/src/media/dmap/simple-dmap-db.vala b/src/media/dmap/simple-dmap-db.vala
new file mode 100644
index 0000000..c78c4bf
--- /dev/null
+++ b/src/media/dmap/simple-dmap-db.vala
@@ -0,0 +1,54 @@
+public class SimpleDMAPDb : GLib.Object, DMAP.Db {
+	// A dumb database that stores everything in an array
+
+	/* FIXME: What is with this unowned? */
+	private Gee.ArrayList<unowned DMAP.Record> db = new Gee.ArrayList<DMAP.Record> ();
+
+	public uint add (DMAP.Record record) {
+		db.add (((DMAP.Record) record));
+		return db.size;
+	}
+
+	public uint add_path (string path) {
+		GLib.error ("add_path not implemented");
+	}
+
+	public uint add_with_id (DMAP.Record record, uint id) {
+		GLib.error ("add_with_id not implemented");
+	}
+
+	public int64 count () {
+		return db.size;
+	}
+
+	public void @foreach (GLib.HFunc func) {
+		int i;
+		for (i = 0; i < db.size; i++) {
+			// NOTE: iPhoto does not like a record ID of 0,
+			// so we pretend to start with 1
+			func ((i + 1).to_pointer (), db[i]);
+		}
+	}
+
+	public void @filtered_foreach (GLib.HRFunc predicate, GLib.HFunc func) {
+		int i;
+		for (i = 0; i < db.size; i++) {
+			// NOTE: iPhoto does not like a record ID of 0,
+			// so we pretend to start with 1
+			if (predicate ((i + 1).to_pointer (), db[i])) {
+				func ((i + 1).to_pointer (), db[i]);
+			}
+		}
+	}
+
+	public unowned DMAP.Record lookup_by_id (uint id) {
+		// NOTE: iPhoto does not like a record ID of 0,
+		// so we pretend to start with 1
+		return db.get ((int) id - 1);
+	}
+
+
+	public uint lookup_id_by_location (string location){
+		GLib.error ("lookup_id_by_location not implemented");
+	}
+}
