changeset 2832:eb4cacfc1d03

branch merge
author Andrew O. Shadoura <bugzilla@tut.by>
date Mon, 14 Jul 2008 01:21:11 +0300
parents 8f28fccf2bdd (current diff) 165d390cbdaa (diff)
children 2d17bc7744d8
files src/streambrowser/shoutcast.xml
diffstat 22 files changed, 587 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
--- a/extra.mk.in	Mon Jul 14 01:16:16 2008 +0300
+++ b/extra.mk.in	Mon Jul 14 01:21:11 2008 +0300
@@ -81,6 +81,7 @@
 BINIO_LIBS ?= @BINIO_LIBS@
 BUILDERS_INCLUDES ?= @BUILDERS_INCLUDES@
 BUILDERS_LDFLAGS ?= @BUILDERS_LDFLAGS@
+BUILDERS_LIBS ?= @BUILDERS_LIBS@
 BUILD_INCLUDED_LIBINTL ?= @BUILD_INCLUDED_LIBINTL@
 CAIRO_CFLAGS ?= @CAIRO_CFLAGS@
 CAIRO_LIBS ?= @CAIRO_LIBS@
@@ -146,7 +147,7 @@
 GNOMEVFS_LIBS ?= @GNOMEVFS_LIBS@
 GTK_CFLAGS ?= @GTK_CFLAGS@
 GTK_LIBS ?= @GTK_LIBS@
-HARDSID_LDADD ?= @HARDSID_LDADD@
+HARDSID_LIBS ?= @HARDSID_LIBS@
 HAVE_ADPLUG_FALSE ?= @HAVE_ADPLUG_FALSE@
 HAVE_ADPLUG_TRUE ?= @HAVE_ADPLUG_TRUE@
 HAVE_ALSA_FALSE ?= @HAVE_ALSA_FALSE@
@@ -250,7 +251,7 @@
 PLUGIN_LDFLAGS ?= @PLUGIN_LDFLAGS@
 POSUB ?= @POSUB@
 RANLIB ?= @RANLIB@
-RESID_LDADD ?= @RESID_LDADD@
+RESID_LIBS ?= @RESID_LIBS@
 SAMPLERATE_CFLAGS ?= @SAMPLERATE_CFLAGS@
 SAMPLERATE_LIBS ?= @SAMPLERATE_LIBS@
 SDL_CFLAGS ?= @SDL_CFLAGS@
@@ -261,9 +262,9 @@
 SHOUT_CFLAGS ?= @SHOUT_CFLAGS@
 SHOUT_LIBS ?= @SHOUT_LIBS@
 SIDPLAY1_INCLUDES ?= @SIDPLAY1_INCLUDES@
-SIDPLAY1_LDADD ?= @SIDPLAY1_LDADD@
+SIDPLAY1_LIBS ?= @SIDPLAY1_LIBS@
 SIDPLAY2_INCLUDES ?= @SIDPLAY2_INCLUDES@
-SIDPLAY2_LDADD ?= @SIDPLAY2_LDADD@
+SIDPLAY2_LIBS ?= @SIDPLAY2_LIBS@
 SNDFILE_CFLAGS ?= @SNDFILE_CFLAGS@
 SNDFILE_LIBS ?= @SNDFILE_LIBS@
 STRIP ?= @STRIP@
--- a/src/madplug/plugin.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/madplug/plugin.c	Mon Jul 14 01:21:11 2008 +0300
@@ -488,8 +488,8 @@
             }
             break;
         }
-    } while (state != STATE_FATAL && tries < 16);
-    /* Give up after 16 failed resync attempts or fatal errors */
+    } while (state != STATE_FATAL && tries < 256);
+    /* Give up after 256 failed resync attempts or fatal errors */
 
     g_message("Rejecting %s (not an MP3 file?)", filename);
     return 0;
--- a/src/sid/xmms-sid.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/sid/xmms-sid.c	Mon Jul 14 01:21:11 2008 +0300
@@ -292,7 +292,7 @@
     Tuple *tmpTuple;
 
     assert(pb);
-    assert(xs_status.sidPlayer);
+    assert(xs_status.sidPlayer != NULL);
     
     XSDEBUG("play '%s'\n", pb->filename);
 
@@ -683,7 +683,7 @@
     xs_tuneinfo_t *tmpInfo;
     gint tmpTune;
 
-    assert(xs_status.sidPlayer);
+    assert(xs_status.sidPlayer != NULL);
 
     if (filename == NULL)
         return NULL;
--- a/src/skins/plugin.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/skins/plugin.c	Mon Jul 14 01:21:11 2008 +0300
@@ -31,17 +31,17 @@
 
 gchar *skins_paths[SKINS_PATH_COUNT] = {};
 
-static Interface skins_gp =
+Interface skins_interface =
 {
     .id = "skinned",
     .desc = "Audacious Skinned GUI",
     .init = skins_init,
-    .fini = skins_cleanup,
-    .conf = skins_configure
+    .fini = skins_cleanup
 };
 
-SIMPLE_INTERFACE_PLUGIN("skinned", &skins_gp);
+SIMPLE_INTERFACE_PLUGIN("skinned", &skins_interface);
 gboolean plugin_is_active = FALSE;
+static GtkWidget *cfgdlg;
 
 static void skins_free_paths(void) {
     int i;
@@ -86,6 +86,10 @@
     init_skins(config.skin);
     mainwin_setup_menus();
 
+    skins_interface.ops->create_prefs_window();
+    cfgdlg = skins_configure();
+    aud_prefswin_page_new(cfgdlg, N_("Skinned Interface"), DATA_DIR "/images/appearance.png");
+
     aud_hook_call("create prefswin", NULL);
 
     if (config.player_visible) mainwin_real_show();
--- a/src/skins/plugin.h	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/skins/plugin.h	Mon Jul 14 01:21:11 2008 +0300
@@ -39,6 +39,7 @@
 };
 
 extern gchar *skins_paths[];
+extern Interface skins_interface;
 
 gboolean skins_init(void);
 gboolean skins_cleanup(void);
--- a/src/skins/ui_main.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/skins/ui_main.c	Mon Jul 14 01:21:11 2008 +0300
@@ -57,6 +57,7 @@
 #include "ui_playlist.h"
 #include "ui_hints.h"
 #include "dnd.h"
+#include "plugin.h"
 #if 0
 #include "configdb.h"
 #include "input.h"
@@ -1697,8 +1698,7 @@
             action_about_audacious();
             break;
         case MAINWIN_GENERAL_PLAYFILE: {
-            gboolean button = FALSE; /* FALSE = NO_PLAY_BUTTON */
-            aud_hook_call("filebrowser show", &button);
+            skins_interface.ops->filebrowser_show(FALSE); /* FALSE = NO_PLAY_BUTTON */
             break;
         }
         case MAINWIN_GENERAL_PLAYLOCATION:
@@ -2702,14 +2702,13 @@
 void
 action_play_file( void )
 {
-    gboolean button = TRUE; /* TRUE = PLAY_BUTTON */
-    aud_hook_call("filebrowser show", &button);
+    skins_interface.ops->filebrowser_show(TRUE); /* TRUE = PLAY_BUTTON */
 }
 
 void
 action_play_location( void )
 {
-    aud_hook_call("urlopener show", NULL);
+    skins_interface.ops->urlopener_show();
 }
 
 void
@@ -2811,8 +2810,7 @@
 void
 action_preferences( void )
 {
-    gboolean show = TRUE;
-    aud_hook_call("prefswin show", &show);
+    skins_interface.ops->show_prefs_window();
 }
 
 void
--- a/src/skins/ui_playlist.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/skins/ui_playlist.c	Mon Jul 14 01:21:11 2008 +0300
@@ -41,6 +41,7 @@
 
 #include "actions-playlist.h"
 #include "dnd.h"
+#include "plugin.h"
 #if 0
 #include "input.h"
 #include "main.h"
@@ -1793,14 +1794,13 @@
 void
 action_playlist_add_files(void)
 {
-    gboolean button = FALSE; /* FALSE = NO_PLAY_BUTTON */
-    aud_hook_call("filebrowser show", &button);
+    skins_interface.ops->filebrowser_show(FALSE); /* FALSE = NO_PLAY_BUTTON */
 }
 
 void
 action_playlist_add_url(void)
 {
-    aud_hook_call("urlopener show", NULL);
+    skins_interface.ops->urlopener_show();
 }
 
 void
--- a/src/streambrowser/Makefile	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/Makefile	Mon Jul 14 01:21:11 2008 +0300
@@ -3,10 +3,12 @@
 SRCS = streambrowser.c	\
        streamdir.c \
        shoutcast.c \
+       xiph.c \
        gui/about_win.c \
        gui/streambrowser_win.c
 
 DATA = images/shoutcast.png \
+       images/xiph.png \
        images/streambrowser-16x16.png \
        images/streambrowser-64x64.png
 
--- a/src/streambrowser/gui/about_win.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/gui/about_win.c	Mon Jul 14 01:21:11 2008 +0300
@@ -1,1 +1,19 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
 
+
--- a/src/streambrowser/gui/about_win.h	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/gui/about_win.h	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #ifndef ABOUT_WIN_H
 #define ABOUT_WIN_H
--- a/src/streambrowser/gui/streambrowser_win.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/gui/streambrowser_win.c	Mon Jul 14 01:21:11 2008 +0300
@@ -38,9 +38,6 @@
 static streamdir_gui_t*	find_streamdir_gui_by_streamdir(streamdir_t *streamdir);
 static gboolean			tree_view_search_equal_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer data);
 
-static gboolean			mystrcasestr(const char *haystack, const char *needle);
-
-
 static GtkWidget*		notebook;
 static GtkWidget*		search_entry;
 static GtkWidget*		add_button;
@@ -191,6 +188,8 @@
 		gtk_tree_store_append(store, &new_iter, &iter);
 		gtk_tree_store_set(store, &new_iter, 0, "gtk-directory", 1, streaminfo->name, 2, streaminfo->current_track, -1);
 	}
+	
+	gtk_tree_path_free(path);
 }
 
 void streambrowser_win_set_update_function(void (*function) (streamdir_t *streamdir, category_t *category, streaminfo_t *streaminfo))
@@ -237,10 +236,11 @@
 	if (fetching) {
 		gchar temp[DEF_STRING_LEN];
 		sprintf(temp, "<span style='italic' weight='heavy'>%s</span>", streaminfo->name);
-		gtk_tree_store_set(store, &iter, 0, "gtk-refresh", 1, temp, 2, "", -1);
+		
+		gtk_tree_store_set(store, &iter, 0, "gtk-refresh", 1, temp, 2, streaminfo->current_track, -1);
 	}
 	else {
-		gtk_tree_store_set(store, &iter, 0, "gtk-directory", 1, streaminfo->name, 2, "", -1);
+		gtk_tree_store_set(store, &iter, 0, "gtk-directory", 1, streaminfo->name, 2, streaminfo->current_track, -1);
 	}
 }
 
@@ -309,13 +309,20 @@
 {
 	if (page_num < 0)
 		return FALSE;
+		
+	/* only enable searching in the current tree (tab) */
 
-	/* update the current selected stream */
+	streamdir_gui_t *streamdir_gui;
+	int i;
 
-	/*
-	streamdir_gui_t *streamdir_gui = g_list_nth_data(streamdir_gui_list, page_num);
-	update_function(streamdir_gui->streamdir, NULL, NULL);
-	*/
+	for (i = 0; i < g_list_length(streamdir_gui_list); i++) {
+		streamdir_gui = g_list_nth_data(streamdir_gui_list, i);
+
+		if (i == page_num) 
+			gtk_tree_view_set_search_column(GTK_TREE_VIEW(streamdir_gui->tree_view), 1);
+		else
+			gtk_tree_view_set_search_column(GTK_TREE_VIEW(streamdir_gui->tree_view), -1);
+	}
 
 	/* clear the search box */
 	gtk_entry_set_text(GTK_ENTRY(search_entry), "");
@@ -344,6 +351,7 @@
 	model = gtk_tree_view_get_model(tree_view);
 	gtk_tree_model_get_iter(model, &iter, path);
 	
+	/* don't fetch a category that has been already fetched */
 	if (gtk_tree_model_iter_has_child(model, &iter)) {
 		gtk_tree_path_free(path);
 		return TRUE;
@@ -420,7 +428,7 @@
 {
 	if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
 		on_add_button_clicked(GTK_BUTTON(add_button), NULL);
-		return FALSE;
+		return TRUE;
 	}
 
 	if (event->keyval == GDK_Escape) {
@@ -428,50 +436,23 @@
 		return FALSE;
 	}
 	
-	/* todo : remove this
-	GtkWidget *table = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
-	streamdir_gui_t *streamdir_gui = find_streamdir_gui_by_table(GTK_TABLE(table));
-	if (streamdir_gui == NULL)
-		return FALSE;
-
-	GtkTreeView *tree_view = GTK_TREE_VIEW(streamdir_gui->tree_view);
-	GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
-	GtkTreeIter iter;
-	GtkTreePath *path;
-	const gchar *key = gtk_entry_get_text(GTK_ENTRY(search_entry));
-	
-	if (!gtk_tree_model_get_iter_first(model, &iter))
-		return FALSE;
-		
-	while (gtk_tree_model_iter_next(model, &iter)) {
-		GValue value = {0, };
-
-		gtk_tree_model_get_value(model, &iter, 1, &value);
-		const gchar *string = g_value_get_string(&value);
-	
-		if (string == NULL || string[0] == '\0' || key == NULL || key[0] == '\0')
-			return FALSE;
-
-		if (mystrcasestr(string, key)) {		gtk_entry_set_text(GTK_ENTRY(search_entry), "");
-
-			path = gtk_tree_model_get_path(model, &iter);	
-			gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
-			gtk_tree_path_free(path);
-		}
-
-		g_value_unset(&value);		
-	}
-	*/
-
 	return FALSE;
 }
 
 static gboolean on_tree_view_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
+	if (event->keyval == GDK_Down || event->keyval == GDK_Up)
+		return FALSE;
+	else
+		if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
+			on_add_button_clicked(GTK_BUTTON(add_button), NULL);
+			return FALSE;
+		}
+
 	gtk_widget_grab_focus(search_entry);
 	on_search_entry_key_pressed(widget, event, data);
 
-	return FALSE;
+	return TRUE;
 }
 
 static streamdir_gui_t *find_streamdir_gui_by_name(gchar *name)
@@ -536,6 +517,9 @@
 
 static gboolean tree_view_search_equal_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer data)
 {
+	if (column == -1)
+		return TRUE;
+
 	GValue value = {0, };
 	gboolean ret;
 	
@@ -552,25 +536,3 @@
 	return ret;
 }
 
-static gboolean mystrcasestr(const char *haystack, const char *needle)
-{
-	int len_h = strlen(haystack) + 1;
-	int len_n = strlen(needle) + 1;
-	int i;
-	
-	char *upper_h = malloc(len_h);
-	char *upper_n = malloc(len_n);
-	
-	for (i = 0; i < len_h; i++)
-		upper_h[i] = toupper(haystack[i]);
-	for (i = 0; i < len_n; i++)
-		upper_n[i] = toupper(needle[i]);
-	
-	char *p = strstr(upper_h, upper_n);
-
-	free(upper_h);
-	free(upper_n);
-	
-	return (gboolean) p;
-}
-
Binary file src/streambrowser/images/shoutcast.png has changed
Binary file src/streambrowser/images/xiph.png has changed
--- a/src/streambrowser/shoutcast.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/shoutcast.c	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #include <string.h>
 #include <glib.h>
@@ -23,11 +41,11 @@
 
 	char temp_pathname[DEF_STRING_LEN];
 	sprintf(temp_pathname, "file://%s", temp_filename);
-	free(temp_filename);
 
 	debug("shoutcast: fetching category file '%s'\n", url);
 	if (!fetch_remote_to_local_file(url, temp_pathname))  {
 		failure("shoutcast: category file '%s' could not be downloaded to '%s'\n", url, temp_pathname);
+		free(temp_filename);
 		return FALSE;
 	}
 	debug("shoutcast: category file '%s' successfuly downloaded to '%s'\n", url, temp_pathname);
@@ -35,9 +53,14 @@
 	xmlDoc *doc = xmlReadFile(temp_pathname, NULL, 0);
 	if (doc == NULL) {
 		failure("shoutcast: failed to read '%s' category file\n", category->name);
+		free(temp_filename);
 		return FALSE;
 	}
 	
+	/* free/remove any existing streaminfos in this category */
+	while (streaminfo_get_count(category) > 0)
+		streaminfo_remove(category, streaminfo_get_by_index(category, 0));
+	
 	xmlNode *root_node = xmlDocGetRootElement(doc);
 	xmlNode *node;
 	
@@ -54,7 +77,7 @@
 
 			debug("shoutcast: fetching stream info for '%s/%d' from '%s'\n", streaminfo_name, streaminfo_id, url);
 
-			streaminfo_t *streaminfo = streaminfo_new(streaminfo_name, streaminfo_playlist_url, streaminfo_current_track);
+			streaminfo_t *streaminfo = streaminfo_new(streaminfo_name, streaminfo_playlist_url, "", streaminfo_current_track);
 			streaminfo_add(category, streaminfo);
 			
 			xmlFree(streaminfo_name);
@@ -65,9 +88,13 @@
 		}
 	}
 	
-	remove(temp_filename);
-	// todo: free the mallocs()
+	xmlFreeDoc(doc);
 	
+	if (remove(temp_filename) != 0) {
+		failure("shoutcast: cannot remove the temporary file: %s\n", strerror(errno));
+	}
+	free(temp_filename);
+
 	return TRUE;
 }
 
@@ -85,11 +112,11 @@
 	
 	char temp_pathname[DEF_STRING_LEN];
 	sprintf(temp_pathname, "file://%s", temp_filename);
-	free(temp_filename);
 	
 	debug("shoutcast: fetching streaming directory file '%s'\n", SHOUTCAST_STREAMDIR_URL);
 	if (!fetch_remote_to_local_file(SHOUTCAST_STREAMDIR_URL, temp_pathname)) {
 		failure("shoutcast: stream directory file '%s' could not be downloaded to '%s'\n", SHOUTCAST_STREAMDIR_URL, temp_pathname);
+		free(temp_filename);
 		return NULL;
 	}
 	debug("shoutcast: stream directory file '%s' successfuly downloaded to '%s'\n", SHOUTCAST_STREAMDIR_URL, temp_pathname);
@@ -97,6 +124,7 @@
 	xmlDoc *doc = xmlReadFile(temp_pathname, NULL, 0);
 	if (doc == NULL) {
 		failure("shoutcast: failed to read stream directory file\n");
+		free(temp_filename);
 		return NULL;
 	}
 	
@@ -120,9 +148,13 @@
 		}
 	}
 
-	// todo: free the mallocs()
+	xmlFreeDoc(doc);
 	
-	remove(temp_filename);
+	if (remove(temp_filename) != 0) {
+		failure("shoutcast: cannot remove the temporary file: %s\n", strerror(errno));
+	}
+	free(temp_filename);
+	
 	debug("shoutcast: streaming directory successfuly loaded\n");
 
 	return streamdir;
--- a/src/streambrowser/shoutcast.h	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/shoutcast.h	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #ifndef SHOUTCAST_H
 #define SHOUTCAST_H
@@ -10,7 +28,6 @@
 #define SHOUTCAST_STREAMDIR_URL		"http://www.shoutcast.com/sbin/newxml.phtml"
 #define SHOUTCAST_CATEGORY_URL		"http://www.shoutcast.com/sbin/newxml.phtml?genre=%s"
 #define SHOUTCAST_STREAMINFO_URL	"http://www.shoutcast.com/sbin/shoutcast-playlist.pls?rn=%s&file=filename.pls"
-#define SHOUTCAST_BUFFER_SIZE		256
 
 
 gboolean							shoutcast_category_fetch(category_t *category);
--- a/src/streambrowser/shoutcast.xml	Mon Jul 14 01:16:16 2008 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding='UTF-8' standalone="yes"?>
-<genrelist>
-<genre name="24h"></genre>
-<genre name="Acid"></genre>
-<genre name="Adult"></genre>
-<genre name="Adulto"></genre>
-<genre name="African"></genre>
-<genre name="Afrikaans"></genre>
-<genre name="Afro"></genre>
-<genre name="Zouk"></genre>
-</genrelist>
-
--- a/src/streambrowser/streambrowser.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/streambrowser.c	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #include <stdlib.h>
 #include <gtk/gtk.h>
@@ -7,6 +25,7 @@
 #include "streambrowser.h"
 #include "streamdir.h"
 #include "shoutcast.h"
+#include "xiph.h"
 #include "gui/streambrowser_win.h"
 #include "gui/about_win.h"
 
@@ -37,7 +56,6 @@
 static GtkWidget *main_menu_item;
 static GQueue *update_thread_data_queue = NULL;
 static GMutex *update_thread_mutex = NULL;
-static gint update_thread_count = 0;
 
 streambrowser_cfg_t streambrowser_cfg;
 
@@ -121,6 +139,28 @@
     return TRUE;
 }
 
+gboolean mystrcasestr(const char *haystack, const char *needle)
+{
+	int len_h = strlen(haystack) + 1;
+	int len_n = strlen(needle) + 1;
+	int i;
+	
+	char *upper_h = malloc(len_h);
+	char *upper_n = malloc(len_n);
+	
+	for (i = 0; i < len_h; i++)
+		upper_h[i] = toupper(haystack[i]);
+	for (i = 0; i < len_n; i++)
+		upper_n[i] = toupper(needle[i]);
+	
+	char *p = strstr(upper_h, upper_n);
+
+	free(upper_h);
+	free(upper_n);
+	
+	return (gboolean) p;
+}
+
 
 static void sb_init()
 {
@@ -238,14 +278,14 @@
           category == NULL ? "" : category->name,
           streaminfo == NULL ? "" : streaminfo->name);
 
-    if (update_thread_count >= MAX_UPDATE_THREADS) {
-        debug("another %d streamdir updates are pending, this request will be dropped\n", update_thread_count);
+    if (g_queue_get_length(update_thread_data_queue) >= MAX_UPDATE_THREADS) {
+        debug("another %d streamdir updates are pending, this request will be dropped\n", g_queue_get_length(update_thread_data_queue));
     }
     else {
         g_mutex_lock(update_thread_mutex);
-            
+        
     	/* do we have a running thread? */
-        if (update_thread_count > 0) {
+        if (g_queue_get_length(update_thread_data_queue) > 0) {
             int i;
             gboolean exists = FALSE;
             update_thread_data_t *update_thread_data;
@@ -263,7 +303,7 @@
             
             /* if no other similar request exists, we enqueue it */
             if (!exists) {
-                debug("another %d streamdir updates are pending, this request will be queued\n", update_thread_count);
+                debug("another %d streamdir updates are pending, this request will be queued\n", g_queue_get_length(update_thread_data_queue));
 
                 update_thread_data = g_malloc(sizeof(update_thread_data_t));
 
@@ -272,7 +312,6 @@
                 update_thread_data->streaminfo = streaminfo;
  
                 g_queue_push_tail(update_thread_data_queue, update_thread_data);
-                update_thread_count++;
             }
             else {
                 debug("this request is already present in the queue, dropping\n");          
@@ -289,7 +328,6 @@
             data->streaminfo = streaminfo;
  
             g_queue_push_tail(update_thread_data_queue, data);
-            update_thread_count++;
 
 			g_thread_create((GThreadFunc) update_thread_core, NULL, FALSE, NULL);
         }
@@ -302,16 +340,16 @@
 {
 	debug("entering update thread core\n");
 
-	/* try to get the last item in the queue */
+	/* try to get the last item in the queue, but don't remove it */
 	g_mutex_lock(update_thread_mutex);
 	update_thread_data_t *data = NULL;
-	if (update_thread_count > 0) {
-		data = g_queue_pop_head(update_thread_data_queue);
+	if (g_queue_get_length(update_thread_data_queue) > 0) {
+		data = g_queue_peek_head(update_thread_data_queue);
 	}
 	g_mutex_unlock(update_thread_mutex);
 
 	/* repetitively process the queue elements, until queue is empty */
-	while (data != NULL && update_thread_count > 0) {
+	while (data != NULL && g_queue_get_length(update_thread_data_queue) > 0) {
 	    /* update a streaminfo - that is - add this streaminfo to playlist */
 		if (data->streaminfo != NULL) {
 	    	gdk_threads_enter();
@@ -339,6 +377,19 @@
 				streambrowser_win_set_category_state(data->streamdir, data->category, FALSE);
 		        gdk_threads_leave();
 		    }
+		    /* xiph */
+		    else if (strncmp(data->streamdir->name, XIPH_NAME, strlen(XIPH_NAME)) == 0) {
+		    	gdk_threads_enter();
+				streambrowser_win_set_category_state(data->streamdir, data->category, TRUE);
+		    	gdk_threads_leave();
+		    	
+		        xiph_category_fetch(data->category);
+
+		        gdk_threads_enter();
+		        streambrowser_win_set_category(data->streamdir, data->category);
+				streambrowser_win_set_category_state(data->streamdir, data->category, FALSE);
+		        gdk_threads_leave();
+		    }
 		}
 		/* update a streamdir */
 		else if (data->streamdir != NULL) {
@@ -351,14 +402,30 @@
 		            gdk_threads_leave();
 		        }
 		    }
+		    /* xiph */
+		    else if (strncmp(data->streamdir->name, XIPH_NAME, strlen(XIPH_NAME)) == 0) {
+		        streamdir_t *streamdir = xiph_streamdir_fetch();
+		        if (streamdir != NULL) {
+		            gdk_threads_enter();
+		            streambrowser_win_set_streamdir(streamdir, XIPH_ICON);
+		            gdk_threads_leave();
+		        }
+		    }
 		}
 		/* update all streamdirs */
 		else {
 		    /* shoutcast */
-		    streamdir_t *shoutcast_streamdir = shoutcast_streamdir_fetch();
-		    if (shoutcast_streamdir != NULL) {
+		    streamdir_t *streamdir = shoutcast_streamdir_fetch();
+		    if (streamdir != NULL) {
 		        gdk_threads_enter();
-		        streambrowser_win_set_streamdir(shoutcast_streamdir, SHOUTCAST_ICON);
+		        streambrowser_win_set_streamdir(streamdir, SHOUTCAST_ICON);
+		        gdk_threads_leave();
+		    }
+		    /* xiph */
+		    streamdir = xiph_streamdir_fetch();
+		    if (streamdir != NULL) {
+		        gdk_threads_enter();
+		        streambrowser_win_set_streamdir(streamdir, XIPH_ICON);
 		        gdk_threads_leave();
 		    }
 		}
@@ -366,13 +433,16 @@
 		g_free(data);
 
 		g_mutex_lock(update_thread_mutex);
-		update_thread_count--;	
+
+		/* remove the just processed data from the queue */
+		g_queue_pop_head(update_thread_data_queue);
 
 		/* try to get the last item in the queue */
-		if (update_thread_count > 0)
-			data = g_queue_pop_head(update_thread_data_queue);
+		if (g_queue_get_length(update_thread_data_queue) > 0)
+			data = g_queue_peek_head(update_thread_data_queue);
 		else
 			data = NULL;
+
 		g_mutex_unlock(update_thread_mutex);
 	}
 
@@ -383,14 +453,22 @@
 
 static void streaminfo_add_to_playlist(streaminfo_t *streaminfo)
 {
-    debug("fetching stream playlist for station '%s' from '%s'\n", streaminfo->name, streaminfo->playlist_url);
-    if (!fetch_remote_to_local_file(streaminfo->playlist_url, PLAYLIST_TEMP_FILE)) {
-        failure("shoutcast: stream playlist '%s' could not be downloaded to '%s'\n", streaminfo->playlist_url, PLAYLIST_TEMP_FILE);
-        return;
-    }
-    debug("stream playlist '%s' successfuly downloaded to '%s'\n", streaminfo->playlist_url, PLAYLIST_TEMP_FILE);
+    if (strlen(streaminfo->playlist_url) > 0) {
+		debug("fetching stream playlist for station '%s' from '%s'\n", streaminfo->name, streaminfo->playlist_url);
+		if (!fetch_remote_to_local_file(streaminfo->playlist_url, PLAYLIST_TEMP_FILE)) {
+		    failure("shoutcast: stream playlist '%s' could not be downloaded to '%s'\n", streaminfo->playlist_url, PLAYLIST_TEMP_FILE);
+		    return;
+		}
+		debug("stream playlist '%s' successfuly downloaded to '%s'\n", streaminfo->playlist_url, PLAYLIST_TEMP_FILE);
 
-    aud_playlist_add_url(aud_playlist_get_active(), PLAYLIST_TEMP_FILE);
+	   	aud_playlist_add(aud_playlist_get_active(), PLAYLIST_TEMP_FILE);
+		debug("stream playlist '%s' added\n", streaminfo->playlist_url);
+	}
+
+	if (strlen(streaminfo->url) > 0) {
+	   	aud_playlist_add(aud_playlist_get_active(), streaminfo->url);
+		debug("stream '%s' added\n", streaminfo->url);
+	}
 }
 
 static void on_plugin_services_menu_item_click()
--- a/src/streambrowser/streambrowser.h	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/streambrowser.h	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #ifndef STREAMBROWSER_H
 #define STREAMBROWSER_H
@@ -28,6 +46,9 @@
 void				failure(const char *fmt, ...);
 gboolean			fetch_remote_to_local_file(gchar *remote_url, gchar *local_url);
 
+	/* returns true if the substring has been found, false otherwise */
+gboolean			mystrcasestr(const char *haystack, const char *needle);
+
 
 #endif	// STREAMBROWSER_H
 
--- a/src/streambrowser/streamdir.c	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/streamdir.c	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #include <string.h>
 #include <glib.h>
@@ -93,11 +111,23 @@
 }
 
 
-streaminfo_t* streaminfo_new(gchar *name, gchar *playlist_url, gchar *current_track)
+streaminfo_t* streaminfo_new(gchar *name, gchar *playlist_url, gchar *url, gchar *current_track)
 {
+	/* replace ampersands with slashes, to avoit gtk/pango markup confusions */
+	int i, count = strlen(name);
+	for (i = 0; i < count; i++)
+		if (name[i] == '&')
+			name[i] = '/';
+
+	count = strlen(current_track);
+	for (i = 0; i < count; i++)
+		if (current_track[i] == '&')
+			current_track[i] = '/';
+
 	streaminfo_t *streaminfo = (streaminfo_t*) g_malloc(sizeof(streaminfo_t));
 	strncpy(streaminfo->name, name, DEF_STRING_LEN);
 	strncpy(streaminfo->playlist_url, playlist_url, DEF_STRING_LEN);
+	strncpy(streaminfo->url, url, DEF_STRING_LEN);
 	strncpy(streaminfo->current_track, current_track, DEF_STRING_LEN);
 
 	return streaminfo;
--- a/src/streambrowser/streamdir.h	Mon Jul 14 01:16:16 2008 +0300
+++ b/src/streambrowser/streamdir.h	Mon Jul 14 01:21:11 2008 +0300
@@ -1,3 +1,21 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
 
 #ifndef STREAMDIR_H
 #define STREAMDIR_H
@@ -11,6 +29,7 @@
 
 	gchar			name[DEF_STRING_LEN];
 	gchar			playlist_url[DEF_STRING_LEN];
+	gchar			url[DEF_STRING_LEN];
 	gchar			current_track[DEF_STRING_LEN];
 
 } streaminfo_t;
@@ -42,7 +61,7 @@
 gint				category_get_count(streamdir_t *streamdir);
 gint				category_get_index(streamdir_t *streamdir, category_t *category);
 
-streaminfo_t*		streaminfo_new(gchar *name, gchar *playlist_url, gchar *current_track);
+streaminfo_t*		streaminfo_new(gchar *name, gchar *playlist_url, gchar *url, gchar *current_track);
 void				streaminfo_delete(streaminfo_t *streaminfo);
 void				streaminfo_free(streaminfo_t *streaminfo);
 void				streaminfo_add(category_t *category, streaminfo_t *streaminfo);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/xiph.c	Mon Jul 14 01:21:11 2008 +0300
@@ -0,0 +1,223 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
+
+#include <string.h>
+#include <glib.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <audacious/plugin.h>
+
+#include "streambrowser.h"
+#include "xiph.h"
+
+
+typedef struct {
+	gchar name[DEF_STRING_LEN];
+	gchar url[DEF_STRING_LEN];
+	gchar current_song[DEF_STRING_LEN];
+	gchar genre[DEF_STRING_LEN];
+} xiph_entry_t;
+
+
+static xiph_entry_t *xiph_entries = NULL;
+static int xiph_entry_count = 0;
+
+typedef struct {
+	gchar *name;
+	gchar *match_string;
+} xiph_category_t;
+
+/* inspired from streamtuner's xiph plugin */
+static xiph_category_t xiph_categories[] = {
+	{ "Alternative", "alternative indie goth college industrial punk hardcore ska" },
+	{ "Electronic", "electronic ambient drum trance techno house downtempo breakbeat jungle garage" },
+	{ "Classical", "classical opera symphonic" },
+	{ "Country", "country swing" },
+	{ "Hip-Hop/Rap", "hip hop rap turntable" },
+	{ "Jazz", "jazz swing" },
+	{ "Oldies", "oldies disco 50s 60s 70s 80s 90s" },
+	{ "Rop", "top pop" },
+	{ "Rock", "rock metal" },
+	{ "R&amp;B/Soul", "r&b funk soul urban" },
+	{ "Spiritual", "spiritual gospel christian muslim jewish religio" },
+	{ "Spoken", "spoken talk comedy" },
+	{ "World", "world reggae island african european east asia" },
+	{ "Other", "various mixed misc eclectic film show instrumental" }
+};
+
+
+// todo: call refresh_streamdir() more often to refresh the current track
+static void refresh_streamdir();
+	/* returns true if any of the words in string1 is present in string2 */
+static gboolean genre_match(gchar *string1, gchar *string2);
+
+
+gboolean xiph_category_fetch(category_t *category)
+{
+	int entryno, categoryno;
+	int xiph_category_count = sizeof(xiph_categories) / sizeof(xiph_category_t);
+	xiph_category_t *xiph_category = NULL;
+	
+	for (categoryno = 0; categoryno < xiph_category_count; categoryno++)
+		if (strcmp(xiph_categories[categoryno].name, category->name) == 0) {
+			xiph_category = xiph_categories + categoryno;
+			break;
+		}
+	
+	/* somehow we've got an invalid/unrecognized category */
+	if (xiph_category == NULL) {
+		failure("xiph: got an unrecognized category: '%s'\n", category->name);
+		return FALSE;
+	}
+	
+	/* free/remove any existing streaminfos in this category */
+	while (streaminfo_get_count(category) > 0)
+		streaminfo_remove(category, streaminfo_get_by_index(category, 0));	
+	
+	/* see what entries match this category */
+	for (entryno = 0; entryno < xiph_entry_count; entryno++) {
+		if (genre_match(xiph_category->match_string, xiph_entries[entryno].genre)) {
+			streaminfo_t *streaminfo = streaminfo_new(xiph_entries[entryno].name, "", xiph_entries[entryno].url, xiph_entries[entryno].current_song);
+			streaminfo_add(category, streaminfo);
+		}
+	}
+	
+	/* if the requested category is the last one in the list ("other"), 
+	   we fill it with all the entries that don't match the rest of categories */
+	if (xiph_category == &xiph_categories[xiph_category_count - 1]) {
+		for (entryno = 0; entryno < xiph_entry_count; entryno++) {
+			gboolean matched = FALSE;
+			
+			for (categoryno = 0; categoryno < xiph_category_count; categoryno++)
+				if (genre_match(xiph_entries[entryno].genre, xiph_categories[categoryno].match_string)) {
+					matched = TRUE;
+					break;
+				}
+			
+			if (!matched) {
+				streaminfo_t *streaminfo = streaminfo_new(xiph_entries[entryno].name, "", xiph_entries[entryno].url, xiph_entries[entryno].current_song);
+				streaminfo_add(category, streaminfo);
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+
+streamdir_t* xiph_streamdir_fetch()
+{
+	streamdir_t *streamdir = streamdir_new(XIPH_NAME);
+	int categno;
+	
+	refresh_streamdir();
+	
+	for (categno = 0; categno < sizeof(xiph_categories) / sizeof(xiph_category_t); categno++) {
+		category_t *category = category_new(xiph_categories[categno].name);
+		category_add(streamdir, category);
+	}
+
+	return streamdir;
+}
+
+static void refresh_streamdir()
+{
+	/* free any previously fetched streamdir data */
+	if (xiph_entries != NULL)
+		free(xiph_entries);
+	xiph_entry_count = 0;
+
+	debug("xiph: fetching streaming directory file '%s'\n", XIPH_STREAMDIR_URL);
+	if (!fetch_remote_to_local_file(XIPH_STREAMDIR_URL, XIPH_TEMP_FILENAME)) {
+		failure("xiph: stream directory file '%s' could not be downloaded to '%s'\n", XIPH_STREAMDIR_URL, XIPH_TEMP_FILENAME);
+		return;
+	}
+	debug("xiph: stream directory file '%s' successfuly downloaded to '%s'\n", XIPH_STREAMDIR_URL, XIPH_TEMP_FILENAME);
+
+	xmlDoc *doc = xmlReadFile(XIPH_TEMP_FILENAME, NULL, 0);
+	if (doc == NULL) {
+		failure("xiph: failed to read stream directory file\n");
+		return;
+	}
+	
+	xmlNode *root_node = xmlDocGetRootElement(doc);
+	xmlNode *node, *child;
+	gchar *content;
+	
+	root_node = root_node->children;
+
+	for (node = root_node; node != NULL; node = node->next) {
+		if (node->type == XML_ELEMENT_NODE) {
+			xiph_entries = realloc(xiph_entries, sizeof(xiph_entry_t) * (xiph_entry_count + 1));
+		
+			for (child = node->children; child != NULL; child = child->next) {
+				if (strcmp((gchar *)child->name, "server_name") == 0) {
+					content = (gchar *) xmlNodeGetContent(child);
+					strcpy(xiph_entries[xiph_entry_count].name, content);
+					xmlFree(content);
+				}
+				else if (strcmp((gchar *)child->name, "listen_url") == 0) {
+					content = (gchar *) xmlNodeGetContent(child);
+					strcpy(xiph_entries[xiph_entry_count].url, content);
+					xmlFree(content);
+				}
+				else if (strcmp((gchar *)child->name, "current_song") == 0) {
+					content = (gchar *) xmlNodeGetContent(child);
+					strcpy(xiph_entries[xiph_entry_count].current_song, content);
+					xmlFree(content);
+				}
+				else if (strcmp((gchar *)child->name, "genre") == 0) {
+					content = (gchar *) xmlNodeGetContent(child);
+					strcpy(xiph_entries[xiph_entry_count].genre, content);
+					xmlFree(content);
+				}
+			}
+			
+			xiph_entry_count++;
+		}
+	}
+
+	xmlFreeDoc(doc);
+	
+	debug("xiph: streaming directory successfuly loaded\n");
+}
+
+static gboolean genre_match(gchar *string1, gchar *string2)
+{
+	char *saveptr = NULL;
+	char *token;
+	gboolean matched = FALSE;
+	char temp1[strlen(string1) + 1];
+	char temp2[strlen(string2) + 1];
+
+	/* these are required for strtok_r to work properly */	
+	strcpy(temp1, string1);
+	strcpy(temp2, string2);
+	
+	token = strtok_r(temp1, " ", &saveptr);
+	while (token != NULL) {
+		if (mystrcasestr(temp2, token))
+			matched = TRUE;
+			
+		token = strtok_r(NULL, " ", &saveptr);
+	}
+	
+	return matched;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/xiph.h	Mon Jul 14 01:21:11 2008 +0300
@@ -0,0 +1,37 @@
+/*
+ * Audacious Streambrowser Plugin
+ *
+ * Copyright (c) 2008 Calin Crisan <ccrisan@gmail.com>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
+
+#ifndef XIPH_H
+#define XIPH_H
+
+#include "streambrowser.h"
+#include "streamdir.h"
+
+#define XIPH_NAME				"Xiph"
+#define XIPH_ICON				DATA_DIR G_DIR_SEPARATOR_S "images" G_DIR_SEPARATOR_S "xiph.png"
+#define XIPH_STREAMDIR_URL		"http://dir.xiph.org/yp.xml"
+#define XIPH_TEMP_FILENAME		"file:///tmp/xiph_yp.xml"
+
+
+gboolean							xiph_category_fetch(category_t *category);
+streamdir_t*						xiph_streamdir_fetch();
+
+
+#endif	// SHOUTCAST_H
+