📄 xiph.c
字号:
/* * Copyright (c) 2003, 2004 Jean-Yves Lefort * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */#include "config.h"#include <string.h>#include <stdlib.h>#include <glib/gi18n.h>#include <libxml/parser.h>#include "streamtuner.h"/*** cpp *********************************************************************/#define XIPH_HOME "http://dir.xiph.org/"#define XIPH_XML "http://dir.xiph.org/yp.xml"#define PARSER_STATE_IS_IN_DIRECTORY(state) \ ((state)->tags && ! (state)->tags->next && ! strcmp((state)->tags->data, "directory"))#define PARSE_ERROR st_handler_notice(xiph_handler, _("parse error at %s"), G_STRLOC)/*** types *******************************************************************/typedef struct{ STStream stream; char *server_name; char *listen_url; char *server_type; char *bitrate; int channels; int samplerate; char *genre; char *current_song;} XiphStream;enum { FIELD_SERVER_NAME, FIELD_LISTEN_URL, FIELD_SERVER_TYPE, FIELD_BITRATE, FIELD_CHANNELS, FIELD_SAMPLERATE, FIELD_GENRE, FIELD_CURRENT_SONG, FIELD_AUDIO /* meta field (bitrate, channels and samplerate) */};enum { TAG_ROOT, TAG_DIRECTORY, TAG_ENTRY, TAG_SERVER_NAME, TAG_LISTEN_URL, TAG_SERVER_TYPE, TAG_BITRATE, TAG_CHANNELS, TAG_SAMPLERATE, TAG_GENRE, TAG_CURRENT_SONG, TAG_UNSUPPORTED};typedef struct{ GSList *tags; GHashTable *stream_properties; GList *streams; char *error;} ParserState; /*** variables ***************************************************************/static STPlugin *xiph_plugin = NULL;static STHandler *xiph_handler = NULL;static struct{ char *name; char *label; char *re; regex_t compiled_re;} stock_genres[] = { { "__alternative", N_("Alternative"), "alternative|indie|goth|college|industrial|punk|hardcore|ska" }, { "__classical", N_("Classical"), "classical|opera|symphonic" }, { "__country", N_("Country"), "country|swing" }, { "__electronic", N_("Electronic"), "electronic|ambient|drum.*bass|trance|techno|house|downtempo|breakbeat|jungle|garage" }, { "__rap", N_("Hip-Hop/Rap"), "hip ?hop|rap|turntabl|old school|new school" }, { "__jazz", N_("Jazz"), "jazz|swing|big ?band" }, { "__oldies", N_("Oldies"), "oldies|disco|50s|60s|70s|80s|90s" }, { "__rock", N_("Pop/Rock"), "pop|rock|top ?40|metal" }, { "__soul", N_("R&B/Soul"), "r ?(&|'? ?n ?'?) ?b|funk|soul|urban" }, { "__spiritual", N_("Spiritual"), "spiritual|gospel|christian|muslim|jewish|religio" }, { "__spoken", N_("Spoken"), "spoken|talk|comedy" }, { "__world", N_("World"), "world|reggae|island|african|european|middle ?east|asia" }, { "__other", N_("Other"), "various|mixed|misc|eclectic|film|show|instrumental" }, { NULL }};static char *search_token = NULL;/*** functions ***************************************************************/static XiphStream *stream_new_cb (gpointer data);static void stream_field_get_cb (XiphStream *stream, STHandlerField *field, GValue *value, gpointer data);static void stream_field_set_cb (XiphStream *stream, STHandlerField *field, const GValue *value, gpointer data);static void stream_stock_field_get_cb (XiphStream *stream, STHandlerStockField stock_field, GValue *value, gpointer data);static void stream_free_cb (XiphStream *stream, gpointer data);static XiphStream *stream_copy (XiphStream *stream);static char *stream_get_audio (XiphStream *stream);static gboolean stream_tune_in_cb (XiphStream *stream, gpointer data, GError **err);static gboolean stream_record_cb (XiphStream *stream, gpointer data, GError **err);static GList *streams_match_genre (GList *streams, regex_t *regexp);static GList *streams_match_any (GList *streams, const char *token);static gboolean utf8_strcasecontains (const char *big, const char *little);static gboolean reload_streams (GList **streams, GError **err);static char *parser_state_get_stream_property_string (ParserState *state, const char *name);static int parser_state_get_stream_property_int (ParserState *state, const char *name);static xmlEntityPtr reload_streams_get_entity_cb (gpointer user_data, const xmlChar *name);static void reload_streams_start_element_cb (gpointer user_data, const xmlChar *name, const xmlChar **atts);static void reload_streams_end_element_cb (gpointer user_data, const xmlChar *name);static void reload_streams_characters_cb (gpointer user_data, const xmlChar *ch, int len);static void reload_streams_warning_cb (gpointer user_data, const char *format, ...) G_GNUC_PRINTF(2, 3);static void reload_streams_error_cb (gpointer user_data, const char *format, ...) G_GNUC_PRINTF(2, 3);static gboolean search_url_cb (STCategory *category);static gboolean check_api_version (GError **err);static void init_handler (void);/*** implementation **********************************************************/static XiphStream *stream_new_cb (gpointer data){ return g_new0(XiphStream, 1);}static voidstream_field_get_cb (XiphStream *stream, STHandlerField *field, GValue *value, gpointer data){ switch (field->id) { case FIELD_SERVER_NAME: g_value_set_string(value, stream->server_name); break; case FIELD_LISTEN_URL: g_value_set_string(value, stream->listen_url); break; case FIELD_SERVER_TYPE: g_value_set_string(value, stream->server_type); break; case FIELD_BITRATE: g_value_set_string(value, stream->bitrate); break; case FIELD_CHANNELS: g_value_set_int(value, stream->channels); break; case FIELD_SAMPLERATE: g_value_set_int(value, stream->samplerate); break; case FIELD_GENRE: g_value_set_string(value, stream->genre); break; case FIELD_CURRENT_SONG: g_value_set_string(value, stream->current_song); break; case FIELD_AUDIO: g_value_take_string(value, stream_get_audio(stream)); break; default: g_assert_not_reached(); }}static voidstream_field_set_cb (XiphStream *stream, STHandlerField *field, const GValue *value, gpointer data){ switch (field->id) { case FIELD_SERVER_NAME: stream->server_name = g_value_dup_string(value); break; case FIELD_LISTEN_URL: stream->listen_url = g_value_dup_string(value); break; case FIELD_SERVER_TYPE: stream->server_type = g_value_dup_string(value); break; case FIELD_BITRATE: stream->bitrate = g_value_dup_string(value); break; case FIELD_CHANNELS: stream->channels = g_value_get_int(value); break; case FIELD_SAMPLERATE: stream->samplerate = g_value_get_int(value); break; case FIELD_GENRE: stream->genre = g_value_dup_string(value); break; case FIELD_CURRENT_SONG: stream->current_song = g_value_dup_string(value); break; default: g_assert_not_reached(); }}static voidstream_stock_field_get_cb (XiphStream *stream, STHandlerStockField stock_field, GValue *value, gpointer data){ switch (stock_field) { case ST_HANDLER_STOCK_FIELD_NAME: g_value_set_string(value, stream->server_name); break; case ST_HANDLER_STOCK_FIELD_GENRE: g_value_set_string(value, stream->genre); break; case ST_HANDLER_STOCK_FIELD_DESCRIPTION: case ST_HANDLER_STOCK_FIELD_HOMEPAGE: /* nop */ break; case ST_HANDLER_STOCK_FIELD_URI_LIST: { GValueArray *value_array; GValue uri_value = { 0, }; value_array = g_value_array_new(1); g_value_init(&uri_value, G_TYPE_STRING); g_value_set_string(&uri_value, stream->listen_url); g_value_array_append(value_array, &uri_value); g_value_unset(&uri_value); g_value_take_boxed(value, value_array); break; } }}static voidstream_free_cb (XiphStream *stream, gpointer data){ g_free(stream->server_name); g_free(stream->listen_url); g_free(stream->server_type); g_free(stream->bitrate); g_free(stream->genre); g_free(stream->current_song); st_stream_free((STStream *) stream);}static XiphStream *stream_copy (XiphStream *stream){ XiphStream *copy; copy = stream_new_cb(NULL); ((STStream *) copy)->name = g_strdup(((STStream *) stream)->name); copy->server_name = g_strdup(stream->server_name); copy->listen_url = g_strdup(stream->listen_url); copy->server_type = g_strdup(stream->server_type); copy->bitrate = g_strdup(stream->bitrate); copy->channels = stream->channels; copy->samplerate = stream->samplerate; copy->genre = g_strdup(stream->genre); copy->current_song = g_strdup(stream->current_song); return copy;}static char *stream_get_audio (XiphStream *stream){ GString *audio; char *str; g_return_val_if_fail(stream != NULL, NULL); audio = g_string_new(NULL); if (stream->bitrate) { if (g_str_has_prefix(stream->bitrate, "Quality")) g_string_append(audio, stream->bitrate); else if (st_str_like(stream->bitrate, ST_NUMERIC)) { int bitrate = atoi(stream->bitrate); if (bitrate > 0 && bitrate < 1000000) /* avoid bogus bitrates */ { /* * Some bitrates are given in bps. To properly convert * bps to kbps, we consider that if the bitrate is * superior to 1000, the unit is bps. * * Also, bitrates such as "16000" probably mean * "16kbps", so we use a kilo of 1000, not 1024. */ if (bitrate > 1000) bitrate /= 1000; str = st_format_bitrate(bitrate); g_string_append(audio, str); g_free(str); } } } if (stream->samplerate > 0) { if (*audio->str) g_string_append(audio, ", "); str = st_format_samplerate(stream->samplerate); g_string_append(audio, str); g_free(str); } if (stream->channels > 0) { if (*audio->str) g_string_append(audio, ", "); str = st_format_channels(stream->channels); g_string_append(audio, str); g_free(str); } if (*audio->str) return g_string_free(audio, FALSE); else { g_string_free(audio, TRUE); return NULL; }}static gbooleanstream_tune_in_cb (XiphStream *stream, gpointer data, GError **err){ return st_action_run("play-stream", stream->listen_url, err);}static gbooleanstream_record_cb (XiphStream *stream, gpointer data, GError **err){ return st_action_run("record-stream", stream->listen_url, err);}static GList *streams_match_genre (GList *streams, regex_t *regexp){ GList *matching = NULL; GList *l; for (l = streams; l; l = l->next) { XiphStream *stream = l->data; if (st_re_match(regexp, stream->genre)) matching = g_list_append(matching, stream_copy(stream)); } return matching;}static GList *streams_match_any (GList *streams, const char *token){ GList *matching = NULL; GList *l; for (l = streams; l; l = l->next) { XiphStream *stream = l->data; if (utf8_strcasecontains(stream->server_name, token) || utf8_strcasecontains(stream->listen_url, token) || utf8_strcasecontains(stream->server_type, token) || utf8_strcasecontains(stream->genre, token) || utf8_strcasecontains(stream->current_song, token)) matching = g_list_append(matching, stream_copy(stream)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -