📄 xiph.c
字号:
return matching;}static gbooleanutf8_strcasecontains (const char *big, const char *little){ gboolean contains; char *normalized_big; char *normalized_little; char *case_normalized_big; char *case_normalized_little; g_return_val_if_fail(big != NULL, FALSE); g_return_val_if_fail(little != NULL, FALSE); normalized_big = g_utf8_normalize(big, -1, G_NORMALIZE_ALL); normalized_little = g_utf8_normalize(little, -1, G_NORMALIZE_ALL); case_normalized_big = g_utf8_casefold(normalized_big, -1); case_normalized_little = g_utf8_casefold(normalized_little, -1); contains = strstr(case_normalized_big, case_normalized_little) != NULL; g_free(normalized_big); g_free(normalized_little); g_free(case_normalized_big); g_free(case_normalized_little); return contains;}static gbooleanreload_multiple_cb (GNode **categories, GHashTable **streams, gpointer data, GError **err){ GList *streams_list = NULL; int i; if (! reload_streams(&streams_list, err)) return FALSE; *streams = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(*streams, "__main", streams_list); if (search_token) g_hash_table_insert(*streams, "__search", streams_match_any(streams_list, search_token)); for (i = 0; stock_genres[i].name; i++) g_hash_table_insert(*streams, stock_genres[i].name, streams_match_genre(streams_list, &stock_genres[i].compiled_re)); return TRUE;}static gbooleanreload_streams (GList **streams, GError **err){ gboolean status; STTransferSession *session; char *body; xmlSAXHandler sax_handler = { NULL }; ParserState state; session = st_transfer_session_new(); status = st_transfer_session_get(session, XIPH_XML, 0, NULL, &body, err); st_transfer_session_free(session); if (! status) return FALSE; sax_handler.getEntity = reload_streams_get_entity_cb; sax_handler.startElement = reload_streams_start_element_cb; sax_handler.endElement = reload_streams_end_element_cb; sax_handler.characters = reload_streams_characters_cb; sax_handler.warning = reload_streams_warning_cb; sax_handler.error = reload_streams_error_cb; sax_handler.fatalError = reload_streams_error_cb; state.tags = NULL; state.stream_properties = NULL; state.streams = NULL; state.error = NULL; status = xmlSAXUserParseMemory(&sax_handler, &state, body, strlen(body)) == 0; g_free(body); g_slist_foreach(state.tags, (GFunc) g_free, NULL); g_slist_free(state.tags); if (state.stream_properties) { g_hash_table_destroy(state.stream_properties); if (status) /* only display warning if the parsing was successful */ PARSE_ERROR; } if (status) *streams = state.streams; else { g_list_foreach(state.streams, (GFunc) stream_free_cb, NULL); g_list_free(state.streams); g_set_error(err, 0, 0, _("unable to parse XML document: %s"), state.error ? state.error : _("unknown error")); } g_free(state.error); return status;}static char *parser_state_get_stream_property_string (ParserState *state, const char *name){ char *str; g_return_val_if_fail(state != NULL, NULL); g_return_val_if_fail(state->stream_properties != NULL, NULL); str = g_strdup(g_hash_table_lookup(state->stream_properties, name)); if (str) { int i; /* remove trailing \r and \n */ for (i = strlen(str) - 1; i >= 0; i--) if (str[i] == '\r' || str[i] == '\n') str[i] = 0; else break; /* replace \r and \n with a space character */ for (i = 0; str[i]; i++) if (str[i] == '\r' || str[i] == '\n') str[i] = ' '; } return str;}static intparser_state_get_stream_property_int (ParserState *state, const char *name){ const char *str; g_return_val_if_fail(state != NULL, 0); g_return_val_if_fail(state->stream_properties != NULL, 0); str = g_hash_table_lookup(state->stream_properties, name); return str ? atoi(str) : 0;}static xmlEntityPtrreload_streams_get_entity_cb (gpointer user_data, const xmlChar *name){ return xmlGetPredefinedEntity(name);}static voidreload_streams_start_element_cb (gpointer user_data, const xmlChar *name, const xmlChar **atts){ ParserState *state = user_data; if (PARSER_STATE_IS_IN_DIRECTORY(state) && ! strcmp(name, "entry")) { if (state->stream_properties) { PARSE_ERROR; g_hash_table_destroy(state->stream_properties); } state->stream_properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); } state->tags = g_slist_prepend(state->tags, g_strdup(name));}static voidreload_streams_end_element_cb (gpointer user_data, const xmlChar *name){ ParserState *state = user_data; char *current_tag; current_tag = state->tags ? state->tags->data : NULL; if (current_tag && ! strcmp(current_tag, name)) { g_free(current_tag); state->tags = g_slist_delete_link(state->tags, state->tags); } else PARSE_ERROR; if (PARSER_STATE_IS_IN_DIRECTORY(state) && ! strcmp(name, "entry")) { char *listen_url; listen_url = parser_state_get_stream_property_string(state, "listen_url"); if (listen_url) { XiphStream *stream; stream = stream_new_cb(NULL); stream->server_name = parser_state_get_stream_property_string(state, "server_name"); stream->listen_url = listen_url; stream->server_type = parser_state_get_stream_property_string(state, "server_type"); stream->bitrate = parser_state_get_stream_property_string(state, "bitrate"); stream->channels = parser_state_get_stream_property_int(state, "channels"); stream->samplerate = parser_state_get_stream_property_int(state, "samplerate"); stream->genre = parser_state_get_stream_property_string(state, "genre"); stream->current_song = parser_state_get_stream_property_string(state, "current_song"); ((STStream *) stream)->name = g_strdup(stream->listen_url); state->streams = g_list_append(state->streams, stream); } else PARSE_ERROR; g_hash_table_destroy(state->stream_properties); state->stream_properties = NULL; }}static voidreload_streams_characters_cb (gpointer user_data, const xmlChar *ch, int len){ ParserState *state = user_data; if (state->stream_properties) { const char *current_tag; char *value; const char *str; char *new_str; g_return_if_fail(state->tags != NULL); current_tag = state->tags->data; value = g_strndup(ch, len); str = g_hash_table_lookup(state->stream_properties, current_tag); if (str) { new_str = g_strconcat(str, value, NULL); g_free(value); } else new_str = value; g_hash_table_insert(state->stream_properties, g_strdup(current_tag), new_str); }}static voidreload_streams_warning_cb (gpointer user_data, const char *format, ...){ va_list args; char *message; va_start(args, format); message = g_strdup_vprintf(format, args); va_end(args); st_handler_notice(xiph_handler, _("XML document: %s"), message); g_free(message);}static voidreload_streams_error_cb (gpointer user_data, const char *format, ...){ ParserState *state = user_data; va_list args; char *message; va_start(args, format); message = g_strdup_vprintf(format, args); va_end(args); if (! state->error) /* only keep the first error for the UI... */ state->error = g_strdup(message); /* ...and display them all */ st_handler_notice(xiph_handler, _("XML document: unrecoverable error: %s"), message); g_free(message);}static gbooleansearch_url_cb (STCategory *category){ char *str; str = st_search_dialog(); if (str) { g_free(category->label); category->label = g_strdup_printf(_("Search results for \"%s\""), str); g_free(search_token); search_token = str; return TRUE; } else return FALSE;}static voidinit_handler (void){ GNode *stock_categories; STCategory *category; int i; STHandlerField *field; xiph_handler = st_handler_new_from_plugin(xiph_plugin); st_handler_set_description(xiph_handler, _("Xiph.org Streaming Directory")); st_handler_set_home(xiph_handler, XIPH_HOME); stock_categories = g_node_new(NULL); category = st_category_new(); category->name = "__main"; category->label = _("All"); g_node_append_data(stock_categories, category); category = st_category_new(); category->name = "__search"; category->label = g_strdup(_("Search")); category->url_cb = search_url_cb; g_node_append_data(stock_categories, category); for (i = 0; stock_genres[i].name; i++) { int status; /* compile the regexp */ status = regcomp(&stock_genres[i].compiled_re, stock_genres[i].re, REG_EXTENDED | REG_ICASE); g_return_if_fail(status == 0); category = st_category_new(); category->name = stock_genres[i].name; category->label = _(stock_genres[i].label); g_node_append_data(stock_categories, category); } st_handler_set_stock_categories(xiph_handler, stock_categories); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_RELOAD_MULTIPLE, reload_multiple_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_NEW, stream_new_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_FIELD_GET, stream_field_get_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_FIELD_SET, stream_field_set_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_STOCK_FIELD_GET, stream_stock_field_get_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_FREE, stream_free_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_TUNE_IN, stream_tune_in_cb, NULL); st_handler_bind(xiph_handler, ST_HANDLER_EVENT_STREAM_RECORD, stream_record_cb, NULL); /* visible fields */ field = st_handler_field_new(FIELD_SERVER_NAME, _("Name"), G_TYPE_STRING, ST_HANDLER_FIELD_VISIBLE); st_handler_field_set_description(field, _("The stream name")); st_handler_add_field(xiph_handler, field); field = st_handler_field_new(FIELD_GENRE, _("Genre"), G_TYPE_STRING, ST_HANDLER_FIELD_VISIBLE); st_handler_field_set_description(field, _("The stream genre")); st_handler_add_field(xiph_handler, field); field = st_handler_field_new(FIELD_CURRENT_SONG, _("Current song"), G_TYPE_STRING, ST_HANDLER_FIELD_VISIBLE); st_handler_field_set_description(field, _("The currently playing song")); st_handler_add_field(xiph_handler, field); field = st_handler_field_new(FIELD_SERVER_TYPE, _("Type"), G_TYPE_STRING, ST_HANDLER_FIELD_VISIBLE); st_handler_field_set_description(field, _("The stream type")); st_handler_add_field(xiph_handler, field); field = st_handler_field_new(FIELD_AUDIO, _("Audio"), G_TYPE_STRING, ST_HANDLER_FIELD_VISIBLE | ST_HANDLER_FIELD_VOLATILE); st_handler_field_set_description(field, _("The stream audio properties")); st_handler_add_field(xiph_handler, field); field = st_handler_field_new(FIELD_LISTEN_URL, _("URL"), G_TYPE_STRING, ST_HANDLER_FIELD_VISIBLE | ST_HANDLER_FIELD_START_HIDDEN); st_handler_field_set_description(field, _("The stream listen URL")); st_handler_add_field(xiph_handler, field); /* invisible fields */ st_handler_add_field(xiph_handler, st_handler_field_new(FIELD_BITRATE, _("Bitrate"), G_TYPE_STRING, 0)); st_handler_add_field(xiph_handler, st_handler_field_new(FIELD_CHANNELS, _("Channels"), G_TYPE_INT, 0)); st_handler_add_field(xiph_handler, st_handler_field_new(FIELD_SAMPLERATE, _("Sample rate"), G_TYPE_INT, 0)); st_handlers_add(xiph_handler);}static gbooleancheck_api_version (GError **err){ if (st_check_api_version(5, 8)) return TRUE; else { g_set_error(err, 0, 0, _("API version mismatch")); return FALSE; }}G_MODULE_EXPORT gbooleanplugin_get_info (STPlugin *plugin, GError **err){ GdkPixbuf *pixbuf; if (! check_api_version(err)) return FALSE; xiph_plugin = plugin; st_plugin_set_name(plugin, "xiph"); st_plugin_set_label(plugin, "Xiph"); pixbuf = st_pixbuf_new_from_file(UIDIR "/xiph.png"); if (pixbuf) { st_plugin_set_icon_from_pixbuf(plugin, pixbuf); g_object_unref(pixbuf); } return TRUE;}G_MODULE_EXPORT gbooleanplugin_init (GError **err){ if (! check_api_version(err)) return FALSE; xmlInitParser(); init_handler(); st_action_register("record-stream", _("Record a stream"), "xterm -e streamripper %q"); st_action_register("play-stream", _("Listen to a stream"), "xmms %q"); return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -