📄 live365.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 LIVE365_ROOT "http://www.live365.com/"#define DIRECTORY_PREFIX "cgi-bin/directory.cgi?genre="#define MAX_STREAMS_PER_PAGE 200 /* enforced by Live365 */#define PARSE_ERROR st_handler_notice(live365_handler, _("parse error at %s"), G_STRLOC)#define CONFIG_USE_MEMBERSHIP "use-membership"#define CONFIG_NAME "name"#define CONFIG_PASSWORD "password"#define CONFIG_STREAM_LIMIT_ENABLED "stream-limit-enabled"#define CONFIG_STREAM_LIMIT "stream-limit"#define MIN_STREAM_LIMIT 0#define MAX_STREAM_LIMIT 9999/*** types *******************************************************************/typedef enum{ ACCESS_ALL, ACCESS_VIP, ACCESS_SUBSCRIPTION} Access;typedef struct{ STStream stream; char *title; char *genre; char *description; char *broadcaster; char *audio; char *homepage; int station_id; Access access; int tlh; double rating;} Live365Stream;typedef struct{ char *sane_id; char *session_id;} LoginInfo;typedef struct{ GNode **categories; GNode *parent;} ReloadCategoriesInfo;typedef struct{ GList **streams; Live365Stream *stream; Live365Stream *finished_stream; gboolean has_next_page;} ReloadStreamsInfo;enum { FIELD_TITLE, FIELD_GENRE, FIELD_DESCRIPTION, FIELD_BROADCASTER, FIELD_AUDIO, FIELD_HOMEPAGE, FIELD_STATION_ID, FIELD_URL, FIELD_ACCESS, FIELD_ACCESS_STRING, FIELD_TLH, FIELD_RATING, FIELD_RATING_STRING};/*** variables ***************************************************************/static STPlugin *live365_plugin = NULL;static STHandler *live365_handler = NULL;static GNode *session_categories = NULL;static GtkWidget *preferences_stream_limit_check;static GtkWidget *preferences_stream_limit_spin;static GtkWidget *preferences_stream_limit_label;static GtkWidget *preferences_use_membership_check;static GtkWidget *preferences_name_label;static GtkWidget *preferences_name_entry;static GtkWidget *preferences_password_label;static GtkWidget *preferences_password_entry;/*** functions ***************************************************************/static Live365Stream *stream_new_cb (gpointer data);static void stream_field_get_cb (Live365Stream *stream, STHandlerField *field, GValue *value, gpointer data);static void stream_field_set_cb (Live365Stream *stream, STHandlerField *field, const GValue *value, gpointer data);static void stream_stock_field_get_cb (Live365Stream *stream, STHandlerStockField stock_field, GValue *value, gpointer data);static void stream_free_cb (Live365Stream *stream, gpointer data);static char *stream_get_url (Live365Stream *stream, gboolean blocking);static gboolean stream_tune_in_cb (Live365Stream *stream, gpointer data, GError **err);static gboolean stream_record_cb (Live365Stream *stream, gpointer data, GError **err);static gboolean stream_browse_cb (Live365Stream *stream, gpointer data, GError **err);static char *get_session_params (gboolean blocking);static gboolean login (const char *name, const char *password, char **sane_id, char **session_id, GError **err);static void login_header_cb (const char *line, gpointer data);static gboolean reload_cb (STCategory *category, GNode **categories, GList **streams, gpointer data, GError **err);static GNode * categories_copy (void);static gboolean categories_copy_cb (GNode *node, gpointer data);static gboolean reload_categories (GNode **categories, GError **err);static void reload_categories_body_cb (const char *line, gpointer data);static gboolean reload_streams (STCategory *category, GList **streams, GError **err);static void reload_streams_body_cb (const char *line, gpointer data);static gboolean 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 preferences_use_membership_toggled_h (GtkToggleButton *button, gpointer user_data);static void preferences_credentials_activate_h (GtkEntry *entry, gpointer user_data);static gboolean preferences_credentials_focus_out_event_h (GtkWidget *widget, GdkEventFocus *event, gpointer user_data);static void init_handler (void);static gboolean check_api_version (GError **err);/*** implementation **********************************************************/static Live365Stream *stream_new_cb (gpointer data){ return g_new0(Live365Stream, 1);}static voidstream_field_get_cb (Live365Stream *stream, STHandlerField *field, GValue *value, gpointer data){ switch (field->id) { case FIELD_TITLE: g_value_set_string(value, stream->title); break; case FIELD_GENRE: g_value_set_string(value, stream->genre); break; case FIELD_DESCRIPTION: g_value_set_string(value, stream->description); break; case FIELD_BROADCASTER: g_value_set_string(value, stream->broadcaster); break; case FIELD_AUDIO: g_value_set_string(value, stream->audio); break; case FIELD_HOMEPAGE: g_value_set_string(value, stream->homepage); break; case FIELD_STATION_ID: g_value_set_int(value, stream->station_id); break; case FIELD_URL: g_value_take_string(value, stream_get_url(stream, FALSE)); break; case FIELD_ACCESS: g_value_set_int(value, stream->access); break; case FIELD_ACCESS_STRING: { const char *str = NULL; switch (stream->access) { case ACCESS_ALL: str = _("All"); break; case ACCESS_VIP: str = _("VIP"); break; case ACCESS_SUBSCRIPTION: str = _("Subscription"); break; } g_value_set_string(value, str); break; } case FIELD_TLH: g_value_set_int(value, stream->tlh); break; case FIELD_RATING: g_value_set_double(value, stream->rating); break; case FIELD_RATING_STRING: g_value_take_string(value, g_strdup_printf("%.2f", stream->rating)); break; default: g_assert_not_reached(); }}static voidstream_field_set_cb (Live365Stream *stream, STHandlerField *field, const GValue *value, gpointer data){ switch (field->id) { case FIELD_TITLE: stream->title = g_value_dup_string(value); break; case FIELD_GENRE: stream->genre = g_value_dup_string(value); break; case FIELD_DESCRIPTION: stream->description = g_value_dup_string(value); break; case FIELD_BROADCASTER: stream->broadcaster = g_value_dup_string(value); break; case FIELD_AUDIO: stream->audio = g_value_dup_string(value); break; case FIELD_HOMEPAGE: stream->homepage = g_value_dup_string(value); break; case FIELD_STATION_ID: stream->station_id = g_value_get_int(value); break; case FIELD_ACCESS: stream->access = g_value_get_int(value); break; case FIELD_TLH: stream->tlh = g_value_get_int(value); break; case FIELD_RATING: stream->rating = g_value_get_double(value); break; default: g_assert_not_reached(); }}static voidstream_stock_field_get_cb (Live365Stream *stream, STHandlerStockField stock_field, GValue *value, gpointer data){ switch (stock_field) { case ST_HANDLER_STOCK_FIELD_NAME: g_value_set_string(value, stream->title); break; case ST_HANDLER_STOCK_FIELD_GENRE: g_value_set_string(value, stream->genre); break; case ST_HANDLER_STOCK_FIELD_DESCRIPTION: g_value_set_string(value, stream->description); break; case ST_HANDLER_STOCK_FIELD_HOMEPAGE: g_value_set_string(value, stream->homepage); break; case ST_HANDLER_STOCK_FIELD_URI_LIST: { GValueArray *value_array; GValue uri_value = { 0, }; char *uri; value_array = g_value_array_new(1); uri = stream_get_url(stream, FALSE); g_value_init(&uri_value, G_TYPE_STRING); g_value_take_string(&uri_value, uri); 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 (Live365Stream *stream, gpointer data){ g_free(stream->title); g_free(stream->genre); g_free(stream->description); g_free(stream->broadcaster); g_free(stream->audio); g_free(stream->homepage); st_stream_free((STStream *) stream);}static char *stream_get_url (Live365Stream *stream, gboolean blocking){ char *params; char *url; g_return_val_if_fail(stream != NULL, NULL); params = get_session_params(blocking); if (params) { url = g_strdup_printf(LIVE365_ROOT "play/%i%s", stream->station_id, params); g_free(params); } else url = g_strdup_printf(LIVE365_ROOT "play/%i", stream->station_id); return url;}static gbooleanstream_tune_in_cb (Live365Stream *stream, gpointer data, GError **err){ char *url; gboolean status; url = stream_get_url(stream, TRUE); status = st_action_run("play-stream", url, err); g_free(url); return status;}static gbooleanstream_record_cb (Live365Stream *stream, gpointer data, GError **err){ char *url; gboolean status; url = stream_get_url(stream, TRUE); status = st_action_run("record-stream", url, err); g_free(url); return status;}static gbooleanstream_browse_cb (Live365Stream *stream, gpointer data, GError **err){ return st_action_run("view-web", stream->homepage, err);}static char *get_session_params (gboolean blocking){ char *config_name; char *config_password; char *name = NULL; char *password = NULL; char *params = NULL; if (! st_handler_config_get_boolean(live365_handler, CONFIG_USE_MEMBERSHIP)) return NULL; config_name = st_handler_config_get_string(live365_handler, CONFIG_NAME); config_password = st_handler_config_get_string(live365_handler, CONFIG_PASSWORD);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -