# HG changeset patch # User Calin Crisan ccrisan@gmail.com # Date 1215910804 -10800 # Node ID cc6f024246097179f4aae2450aa1fa845f017441 # Parent a03d670e8752960f74680b716f04f10802863806 added initial support for xiph streaming directory; small bug fixes & code cleanups diff -r a03d670e8752 -r cc6f02424609 src/streambrowser/Makefile --- a/src/streambrowser/Makefile Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/Makefile Sun Jul 13 04:00:04 2008 +0300 @@ -3,6 +3,7 @@ SRCS = streambrowser.c \ streamdir.c \ shoutcast.c \ + xiph.c \ gui/about_win.c \ gui/streambrowser_win.c diff -r a03d670e8752 -r cc6f02424609 src/streambrowser/gui/streambrowser_win.c --- a/src/streambrowser/gui/streambrowser_win.c Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/gui/streambrowser_win.c Sun Jul 13 04:00:04 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)) @@ -309,13 +308,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 +350,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; @@ -428,41 +435,6 @@ 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; } @@ -536,6 +508,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 +527,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 a03d670e8752 -r cc6f02424609 src/streambrowser/shoutcast.c --- a/src/streambrowser/shoutcast.c Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/shoutcast.c Sun Jul 13 04:00:04 2008 +0300 @@ -57,6 +57,10 @@ 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; @@ -73,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); @@ -84,12 +88,13 @@ } } + xmlFreeDoc(doc); + if (remove(temp_filename) != 0) { failure("shoutcast: cannot remove the temporary file: %s\n", strerror(errno)); } free(temp_filename); - // todo: free the xml mallocs() - + return TRUE; } @@ -143,7 +148,7 @@ } } - // todo: free the xml mallocs() + xmlFreeDoc(doc); if (remove(temp_filename) != 0) { failure("shoutcast: cannot remove the temporary file: %s\n", strerror(errno)); diff -r a03d670e8752 -r cc6f02424609 src/streambrowser/shoutcast.h --- a/src/streambrowser/shoutcast.h Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/shoutcast.h Sun Jul 13 04:00:04 2008 +0300 @@ -28,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 a03d670e8752 -r cc6f02424609 src/streambrowser/streambrowser.c --- a/src/streambrowser/streambrowser.c Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/streambrowser.c Sun Jul 13 04:00:04 2008 +0300 @@ -25,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" @@ -139,6 +140,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() { @@ -261,7 +284,7 @@ } else { g_mutex_lock(update_thread_mutex); - + /* do we have a running thread? */ if (update_thread_count > 0) { int i; @@ -320,11 +343,11 @@ { 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); + data = g_queue_peek_head(update_thread_data_queue); } g_mutex_unlock(update_thread_mutex); @@ -357,6 +380,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) { @@ -369,14 +405,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(); } } @@ -384,6 +436,9 @@ g_free(data); g_mutex_lock(update_thread_mutex); + + /* remove the just processed data from the queue */ + g_queue_pop_head(update_thread_data_queue); update_thread_count--; /* try to get the last item in the queue */ @@ -401,14 +456,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 a03d670e8752 -r cc6f02424609 src/streambrowser/streambrowser.h --- a/src/streambrowser/streambrowser.h Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/streambrowser.h Sun Jul 13 04:00:04 2008 +0300 @@ -46,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 a03d670e8752 -r cc6f02424609 src/streambrowser/streamdir.c --- a/src/streambrowser/streamdir.c Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/streamdir.c Sun Jul 13 04:00:04 2008 +0300 @@ -111,11 +111,12 @@ } -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) { 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 a03d670e8752 -r cc6f02424609 src/streambrowser/streamdir.h --- a/src/streambrowser/streamdir.h Sat Jul 12 16:00:03 2008 +0200 +++ b/src/streambrowser/streamdir.h Sun Jul 13 04:00:04 2008 +0300 @@ -29,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; @@ -60,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 a03d670e8752 -r cc6f02424609 src/streambrowser/xiph.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/streambrowser/xiph.c Sun Jul 13 04:00:04 2008 +0300 @@ -0,0 +1,148 @@ +/* + * 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; +static gchar *categories[] = { + // todo: complete this list + "alternative", + "dance", + "techno", + "rock", + "pop" +}; + + +void refresh_streamdir(); + + + +gboolean xiph_category_fetch(category_t *category) +{ + int entryno; + + /* see what entries match this category */ + for (entryno = 0; entryno < xiph_entry_count; entryno++) { + + if (mystrcasestr(xiph_entries[entryno].genre, category->name)) { + 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(categories) / sizeof(gchar *); categno++) { + category_t *category = category_new(categories[categno]); + category_add(streamdir, category); + } + + return streamdir; +} + +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"); +} + diff -r a03d670e8752 -r cc6f02424609 src/streambrowser/xiph.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/streambrowser/xiph.h Sun Jul 13 04:00:04 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 +