📄 shoutcast.c
字号:
/* * Copyright (c) 2002, 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 "streamtuner.h"/*** cpp *********************************************************************/#define SHOUTCAST_ROOT "http://www.shoutcast.com/"#define MAX_STREAMS_PER_PAGE 100 /* enforced by SHOUTcast */#define PARSE_ERROR st_handler_notice(shoutcast_handler, _("parse error at %s"), G_STRLOC)#define CONFIG_STREAM_LIMIT_ENABLED "stream-limit-enabled"#define CONFIG_STREAM_LIMIT "stream-limit"#define MIN_STREAM_LIMIT 0#define MAX_STREAM_LIMIT 9999/*** type definitions ********************************************************/typedef struct{ STStream stream; char *genre; char *description; char *now_playing; int listeners; int max; int bitrate; char *url_postfix; char *homepage; /* * url_list may be set after the stream has entered streamtuner, so * we need to protect it. */ GSList *url_list; GMutex *url_list_mutex;} SHOUTcastStream;enum { FIELD_GENRE, FIELD_DESCRIPTION, FIELD_NOW_PLAYING, FIELD_LISTENERS, FIELD_MAX, FIELD_BITRATE, FIELD_URL_POSTFIX, FIELD_HOMEPAGE, FIELD_URL_LIST};typedef struct{ GNode **categories; GList **streams; int page; int npages; GNode *parent_node; SHOUTcastStream *stream;} ReloadInfo;/*** variable declarations ***************************************************/static STPlugin *shoutcast_plugin = NULL;static STHandler *shoutcast_handler = NULL;static GtkWidget *preferences_stream_limit_check;static GtkWidget *preferences_stream_limit_spin;static GtkWidget *preferences_stream_limit_label;/*** function declarations ***************************************************/static gboolean reload_cb (STCategory *category, GNode **categories, GList **streams, gpointer data, GError **err);static void reload_body_cb (const char *line, gpointer data);static SHOUTcastStream *stream_new_cb (gpointer data);static void stream_field_get_cb (SHOUTcastStream *stream, STHandlerField *field, GValue *value, gpointer data);static void stream_field_set_cb (SHOUTcastStream *stream, STHandlerField *field, const GValue *value, gpointer data);static void stream_stock_field_get_cb (SHOUTcastStream *stream, STHandlerStockField stock_field, GValue *value, gpointer data);static void stream_free_cb (SHOUTcastStream *stream, gpointer data);static void stream_get_url_list (SHOUTcastStream *stream, GValue *value);static gboolean stream_resolve (SHOUTcastStream *stream, GError **err);static gboolean stream_resolve_cb (SHOUTcastStream *stream, gpointer data, GError **err);static gboolean stream_tune_in_cb (SHOUTcastStream *stream, gpointer data, GError **err);static gboolean stream_record_cb (SHOUTcastStream *stream, gpointer data, GError **err);static gboolean stream_browse_cb (SHOUTcastStream *stream, gpointer data, GError **err);static int search_url_cb (STCategory *category);static GtkWidget * preferences_widget_new_cb (gpointer data);static void preferences_update_sensitivity (void);static void preferences_stream_limit_toggled_h (GtkToggleButton *button, gpointer user_data);static void preferences_stream_limit_changed_h (GtkSpinButton *spin, gpointer user_data);static void init_handler (void);static gboolean check_api_version (GError **err);/*** implementation **********************************************************/static SHOUTcastStream *stream_new_cb (gpointer data){ SHOUTcastStream *stream; stream = g_new0(SHOUTcastStream, 1); stream->url_list_mutex = g_mutex_new(); return stream;}static voidstream_field_get_cb (SHOUTcastStream *stream, STHandlerField *field, GValue *value, gpointer data){ switch (field->id) { case FIELD_GENRE: g_value_set_string(value, stream->genre); break; case FIELD_DESCRIPTION: g_value_set_string(value, stream->description); break; case FIELD_NOW_PLAYING: g_value_set_string(value, stream->now_playing); break; case FIELD_LISTENERS: g_value_set_int(value, stream->listeners); break; case FIELD_MAX: g_value_set_int(value, stream->max); break; case FIELD_BITRATE: g_value_set_int(value, stream->bitrate); break; case FIELD_URL_POSTFIX: g_value_set_string(value, stream->url_postfix); break; case FIELD_HOMEPAGE: g_value_set_string(value, stream->homepage); break; case FIELD_URL_LIST: stream_get_url_list(stream, value); break; default: g_return_if_reached(); }}static voidstream_field_set_cb (SHOUTcastStream *stream, STHandlerField *field, const GValue *value, gpointer data){ switch (field->id) { case FIELD_GENRE: stream->genre = g_value_dup_string(value); break; case FIELD_DESCRIPTION: stream->description = g_value_dup_string(value); break; case FIELD_NOW_PLAYING: stream->now_playing = g_value_dup_string(value); break; case FIELD_LISTENERS: stream->listeners = g_value_get_int(value); break; case FIELD_MAX: stream->max = g_value_get_int(value); break; case FIELD_BITRATE: stream->bitrate = g_value_get_int(value); break; case FIELD_URL_POSTFIX: stream->url_postfix = g_value_dup_string(value); break; case FIELD_HOMEPAGE: stream->homepage = g_value_dup_string(value); break; case FIELD_URL_LIST: { GValueArray *value_array; int i; value_array = g_value_get_boxed(value); for (i = 0; i < value_array->n_values; i++) { GValue *url_value = g_value_array_get_nth(value_array, i); stream->url_list = g_slist_append(stream->url_list, g_value_dup_string(url_value)); } break; } default: g_return_if_reached(); }}static voidstream_stock_field_get_cb (SHOUTcastStream *stream, STHandlerStockField stock_field, GValue *value, gpointer data){ switch (stock_field) { case ST_HANDLER_STOCK_FIELD_NAME: g_value_set_string(value, stream->description); break; case ST_HANDLER_STOCK_FIELD_GENRE: g_value_set_string(value, stream->genre); break; case ST_HANDLER_STOCK_FIELD_DESCRIPTION: /* nop */ break; case ST_HANDLER_STOCK_FIELD_HOMEPAGE: g_value_set_string(value, stream->homepage); break; case ST_HANDLER_STOCK_FIELD_URI_LIST: stream_get_url_list(stream, value); break; }}static voidstream_free_cb (SHOUTcastStream *stream, gpointer data){ GSList *l; g_free(stream->genre); g_free(stream->description); g_free(stream->now_playing); g_free(stream->url_postfix); g_free(stream->homepage); for (l = stream->url_list; l; l = l->next) g_free(l->data); g_slist_free(stream->url_list); g_mutex_free(stream->url_list_mutex); st_stream_free((STStream *) stream);}static voidstream_get_url_list (SHOUTcastStream *stream, GValue *value){ GValueArray *value_array; GSList *l; g_return_if_fail(stream != NULL); g_return_if_fail(value != NULL); value_array = g_value_array_new(0); g_mutex_lock(stream->url_list_mutex); for (l = stream->url_list; l; l = l->next) { GValue url_value = { 0, }; g_value_init(&url_value, G_TYPE_STRING); g_value_set_string(&url_value, l->data); g_value_array_append(value_array, &url_value); g_value_unset(&url_value); } g_mutex_unlock(stream->url_list_mutex); g_value_take_boxed(value, value_array);}static gbooleanstream_resolve (SHOUTcastStream *stream, GError **err){ gboolean already_resolved; STTransferSession *session; char *url; char *playlist; gboolean status; g_return_val_if_fail(stream != NULL, FALSE); g_mutex_lock(stream->url_list_mutex); already_resolved = stream->url_list != NULL; g_mutex_unlock(stream->url_list_mutex); if (already_resolved) return TRUE; /* already resolved */ url = g_strconcat(SHOUTCAST_ROOT, stream->url_postfix, NULL); session = st_transfer_session_new(); status = st_transfer_session_get(session, url, ST_TRANSFER_UTF8, NULL, &playlist, err); st_transfer_session_free(session); g_free(url); if (status) { gboolean empty; g_mutex_lock(stream->url_list_mutex); stream->url_list = st_pls_parse(playlist); empty = stream->url_list == NULL; g_mutex_unlock(stream->url_list_mutex); g_free(playlist); if (empty) { g_set_error(err, 0, 0, _("stream is empty")); return FALSE; } } return status;}static gbooleanstream_resolve_cb (SHOUTcastStream *stream, gpointer data, GError **err){ return stream_resolve(stream, err);}static gbooleanstream_tune_in_cb (SHOUTcastStream *stream, gpointer data, GError **err){ char *m3uname; gboolean status; if (! stream_resolve(stream, err)) return FALSE; g_mutex_lock(stream->url_list_mutex); m3uname = st_m3u_mktemp("streamtuner.shoutcast.XXXXXX", stream->url_list, err); g_mutex_unlock(stream->url_list_mutex); if (! m3uname) return FALSE; status = st_action_run("play-m3u", m3uname, err); g_free(m3uname); return status;}static gbooleanstream_record_cb (SHOUTcastStream *stream, gpointer data, GError **err){ gboolean status; if (! stream_resolve(stream, err)) return FALSE; g_mutex_lock(stream->url_list_mutex); status = st_action_run("record-stream", stream->url_list->data, err); g_mutex_unlock(stream->url_list_mutex); return status;}static gbooleanstream_browse_cb (SHOUTcastStream *stream, gpointer data, GError **err){ if (! stream->homepage) /* older versions of the plugin didn't have this field */ { g_set_error(err, 0, 0, _("the stream is too old, please reload")); return FALSE; } return st_action_run("view-web", stream->homepage, err);}static gbooleanreload_cb (STCategory *category, GNode **categories, GList **streams, gpointer data, GError **err){ ReloadInfo info; STTransferSession *session; gboolean status; int stream_limit; int requested_streams = 0; int received_streams = 0; g_return_val_if_fail(category != NULL, FALSE); g_return_val_if_fail(category->url_postfix != NULL, FALSE); *categories = g_node_new(NULL); *streams = NULL; info.categories = categories;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -