changeset 1568:0a927f814a12 trunk

[svn] - experimental XSPF support
author nenolod
date Thu, 10 Aug 2006 22:26:50 -0700
parents bbc9d6fad927
children 3af18d169044
files ChangeLog Plugins/Container/xspf/Makefile Plugins/Container/xspf/xspf.c configure.ac
diffstat 4 files changed, 307 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Aug 10 21:00:59 2006 -0700
+++ b/ChangeLog	Thu Aug 10 22:26:50 2006 -0700
@@ -1,3 +1,12 @@
+2006-08-11 04:00:59 +0000  William Pitcock <nenolod@nenolod.net>
+  revision [2048]
+  - add missing $(CONTAINER_PLUGINS).
+  
+
+  Changes:        Modified:
+  +1 -0           trunk/mk/rules.mk.in  
+
+
 2006-08-11 03:44:47 +0000  William Pitcock <nenolod@nenolod.net>
   revision [2046]
   - pls and m3u is now fully modular
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Container/xspf/Makefile	Thu Aug 10 22:26:50 2006 -0700
@@ -0,0 +1,16 @@
+include ../../../mk/rules.mk
+include ../../../mk/objective.mk
+
+OBJECTIVE_LIBS = libxspf.so
+
+LIBDIR = $(plugindir)/$(CONTAINER_PLUGIN_DIR)
+
+SOURCES = xspf.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += -fPIC -DPIC $(GTK_CFLAGS) $(ARCH_DEFINES) $(XML_CPPFLAGS) -I../../../intl -I../../..
+
+CXXFLAGS = $(CFLAGS)
+
+LIBADD = $(GTK_LIBS) $(XML_LIBS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Container/xspf/xspf.c	Thu Aug 10 22:26:50 2006 -0700
@@ -0,0 +1,264 @@
+/*
+ * Audacious: A cross-platform multimedia player
+ * Copyright (c) 2006 William Pitcock, Tony Vroon, George Averill,
+ *                    Giacomo Lozito, Derek Pomery and Yoshiki Yazawa.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+#include "audacious/main.h"
+#include "audacious/util.h"
+#include "audacious/playlist.h"
+#include "audacious/playlist_container.h"
+#include "audacious/plugin.h"
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlreader.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+/* Borrowed from BMPx. */
+static int
+register_namespaces (xmlXPathContextPtr xpathCtx, const xmlChar* nsList)
+{
+    xmlChar* nsListDup;
+    xmlChar* prefix;
+    xmlChar* href;
+    xmlChar* next;
+
+    nsListDup = xmlStrdup(nsList);
+    if(nsListDup == NULL)
+    {
+	g_warning (G_STRLOC ": unable to strdup namespaces list");
+	return(-1);
+    }
+
+    next = nsListDup;
+    while (next != NULL)
+    {
+	while((*next) == ' ') next++;
+	if((*next) == '\0') break;
+
+	prefix = next;
+	next = (xmlChar*)xmlStrchr(next, '=');
+
+	if (next == NULL)
+	{
+	    g_warning (G_STRLOC ": invalid namespaces list format");
+  	    xmlFree (nsListDup);
+            return(-1);
+        }
+        *(next++) = '\0';
+
+        href = next;
+        next = (xmlChar*)xmlStrchr(next, ' ');
+        if (next != NULL)
+        {
+            *(next++) = '\0';
+        }
+
+        // do register namespace
+        if(xmlXPathRegisterNs(xpathCtx, prefix, href) != 0)
+        {
+            g_warning (G_STRLOC ": unable to register NS with prefix=\"%s\" and href=\"%s\"", prefix, href);
+            xmlFree(nsListDup);
+            return(-1);
+        }
+    }
+
+    xmlFree(nsListDup);
+    return(0);
+}
+
+/* Also borrowed from BMPx. */
+xmlXPathObjectPtr
+xml_execute_xpath_expression (xmlDocPtr doc,
+                              const xmlChar * xpathExpr,
+                              const xmlChar * nsList)
+{
+    xmlXPathContextPtr xpathCtx;
+    xmlXPathObjectPtr xpathObj;
+
+    /*
+     * Create xpath evaluation context
+     */
+    xpathCtx = xmlXPathNewContext (doc);
+    if (xpathCtx == NULL)
+    {
+        return NULL;
+    }
+
+    if (nsList)
+        register_namespaces (xpathCtx, nsList);
+
+    /*
+     * Evaluate xpath expression
+     */
+    xpathObj = xmlXPathEvalExpression (xpathExpr, xpathCtx);
+    if (xpathObj == NULL)
+    {
+        xmlXPathFreeContext (xpathCtx);
+        return NULL;
+    }
+
+    /*
+     * Cleanup
+     */
+    xmlXPathFreeContext (xpathCtx);
+
+    return xpathObj;
+}
+
+static void
+playlist_load_xspf(const gchar * filename, gint pos)
+{
+	xmlDocPtr	  doc;
+	xmlXPathObjectPtr xpathObj;
+	xmlNodeSetPtr     n;
+	gint i;
+
+	doc = xmlParseFile(filename);
+	if (doc == NULL)
+		return;
+
+	/* TODO: what about xspf:artist, xspf:title, xspf:length? */
+	xpathObj = xml_execute_xpath_expression(doc, "//xspf:location", "xspf=http://xspf.org/ns/0/");
+	if (xpathObj == NULL)
+		return;
+
+	n = xpathObj->nodesetval;
+	if (n == NULL)
+		return;
+
+	for (i = 0; i < n->nodeNr; i++)
+	{
+		char *uri = (char *)XML_GET_CONTENT(n->nodeTab[i]->children);
+		playlist_ins(uri, pos);
+		++pos;
+		g_free(uri);
+	}
+
+	xmlXPathFreeObject(xpathObj);
+}
+
+#define XSPF_ROOT_NODE_NAME "playlist"
+#define XSPF_XMLNS "http://xspf.org/ns/0/"
+
+static void
+playlist_save_xspf(const gchar *filename, gint pos)
+{
+	xmlDocPtr doc;
+	xmlNodePtr rootnode, tmp, tracklist;
+	GList *node;
+
+	doc = xmlNewDoc("1.0");
+
+	rootnode = xmlNewNode(NULL, XSPF_ROOT_NODE_NAME);
+	xmlSetProp(rootnode, "xmlns", XSPF_XMLNS);
+	xmlSetProp(rootnode, "version", "1");
+	xmlDocSetRootElement(doc, rootnode);
+
+	tmp = xmlNewNode(NULL, "creator");
+	xmlAddChild(tmp, xmlNewText(PACKAGE "-" VERSION));
+	xmlAddChild(rootnode, tmp);
+
+	tracklist = xmlNewNode(NULL, "trackList");
+	xmlAddChild(rootnode, tracklist);
+
+	for (node = playlist_get(); node != NULL; node = g_list_next(node))
+	{
+		PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
+		xmlNodePtr track, location;
+
+		track = xmlNewNode(NULL, "track");
+		location = xmlNewNode(NULL, "location");
+
+		xmlAddChild(location, xmlNewText(entry->filename));
+		xmlAddChild(track, location);
+		xmlAddChild(tracklist, track);
+
+		/* do we have a tuple? */
+		if (entry->tuple != NULL)
+		{
+			if (entry->tuple->performer != NULL)
+			{
+				tmp = xmlNewNode(NULL, "creator");
+				xmlAddChild(tmp, xmlNewText(entry->tuple->performer));
+				xmlAddChild(track, tmp);
+			}
+
+			if (entry->tuple->album_name != NULL)
+			{
+				tmp = xmlNewNode(NULL, "album");
+				xmlAddChild(tmp, xmlNewText(entry->tuple->album_name));
+				xmlAddChild(track, tmp);
+			}
+
+			if (entry->tuple->track_name != NULL)
+			{
+				tmp = xmlNewNode(NULL, "title");
+				xmlAddChild(tmp, xmlNewText(entry->tuple->track_name));
+				xmlAddChild(track, tmp);
+			}
+		}
+	}
+
+	xmlSaveFile(filename, doc);
+	xmlFreeDoc(doc);
+}
+
+PlaylistContainer plc_xspf = {
+	.name = "XSPF Playlist Format",
+	.ext = "xspf",
+	.plc_read = playlist_load_xspf,
+	.plc_write = playlist_save_xspf,
+};
+
+static void init(void)
+{
+	playlist_container_register(&plc_xspf);
+}
+
+static void cleanup(void)
+{
+	playlist_container_unregister(&plc_xspf);
+}
+
+LowlevelPlugin llp_xspf = {
+	NULL,
+	NULL,
+	"XSPF Playlist Format",
+	init,
+	cleanup,
+};
+
+LowlevelPlugin *get_lplugin_info(void)
+{
+	return &llp_xspf;
+}
--- a/configure.ac	Thu Aug 10 21:00:59 2006 -0700
+++ b/configure.ac	Thu Aug 10 22:26:50 2006 -0700
@@ -914,29 +914,37 @@
 AC_ARG_ENABLE(paranormal,
 [  --disable-paranormal    disable paranormal vis plugin. (default=enabled)],
 [have_paranormal=$enableval],
-[
-  if test "x$ap_disable_paranormal" = "xyes"; then
+[have_paranormal=yes])
+
+if test "x$ap_disable_paranormal" = "xyes"; then
     have_paranormal=no
     AC_MSG_RESULT([*** paranormal plugin disabled by request ***])
-  else
+else
     have_paranormal=yes
     AM_PATH_SDL(1.2.5, , [
 	AC_MSG_WARN([*** SDL is not installed. Paranormal will not be built. ***])
 	have_paranormal="no"
     ])
+fi
 
-    AM_PATH_XML2(2.0.0, , [
-	AC_MSG_WARN([*** libxml2 is not installed. Paranormal will not be built. ***])
+AC_ARG_ENABLE(xspf,
+[  --disable-xspf          disable XSPF support. (default=enabled)],
+[have_xspf=$enableval], [have_xspf=yes])
+
+AM_PATH_XML2(2.0.0, , [
+	AC_MSG_WARN([*** libxml2 is not installed. Paranormal nor XSPF support will not be built. ***])
 	have_paranormal="no"
-    ])
-  fi
-]
-)
+	have_xspf="no"
+])
 
 if test "$have_paranormal" = "yes"; then
 	VISUALIZATION_PLUGINS="$VISUALIZATION_PLUGINS paranormal"
 fi
 
+if test "$have_xspf" = "yes"; then
+	CONTAINER_PLUGINS="$CONTAINER_PLUGINS xspf"
+fi
+
 dnl *** End of all plugin checks ***
 
 AC_SUBST(ARCH_DEFINES)
@@ -1156,6 +1164,7 @@
 echo "  -------------"
 echo "  Winamp PLS playlist format (pls):       yes"
 echo "  M3U playlist format (m3u):              yes"
+echo "  XML Sharable Playlist Format (xspf):    $have_xspf"
 echo
 
 if test "$beep_cv_lib_xlibs_threadsafe" = "no"; then