# HG changeset patch # User Andrew O. Shadoura # Date 1215987671 -10800 # Node ID eb4cacfc1d035ede0da29723fb2b1792f666aac9 # Parent 8f28fccf2bdd5bd65f86733b832a090a1a504ea5# Parent 165d390cbdaafea7711cd683fb5c23bb23119ff8 branch merge diff -r 8f28fccf2bdd -r eb4cacfc1d03 extra.mk.in --- 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@ diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/madplug/plugin.c --- 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; diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/sid/xmms-sid.c --- 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; diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/skins/plugin.c --- 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(); diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/skins/plugin.h --- 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); diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/skins/ui_main.c --- 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 diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/skins/ui_playlist.c --- 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 diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/Makefile --- 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 diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/gui/about_win.c --- 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 + * + * 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 . + */ + diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/gui/about_win.h --- 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 + * + * 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 . + */ + #ifndef ABOUT_WIN_H #define ABOUT_WIN_H diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/gui/streambrowser_win.c --- 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, "%s", 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; -} - diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/images/shoutcast.png Binary file src/streambrowser/images/shoutcast.png has changed diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/images/xiph.png Binary file src/streambrowser/images/xiph.png has changed diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/shoutcast.c --- 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 + * + * 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 . + */ + #include #include @@ -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; diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/shoutcast.h --- 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 + * + * 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 . + */ + #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); diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/shoutcast.xml --- 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 @@ - - - - - - - - - - - - diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/streambrowser.c --- 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 + * + * 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 . + */ + #include #include @@ -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() diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/streambrowser.h --- 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 + * + * 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 . + */ + #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 diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/streamdir.c --- 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 + * + * 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 . + */ + #include #include @@ -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; diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/streamdir.h --- 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 + * + * 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 . + */ + #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); diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/xiph.c --- /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 + * + * 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 . + */ + + +#include +#include +#include +#include +#include + +#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&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; +} + diff -r 8f28fccf2bdd -r eb4cacfc1d03 src/streambrowser/xiph.h --- /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 + * + * 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 . + */ + + +#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 +