summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan A. Suarez Romero <jasuarez@igalia.com>2011-12-13 15:19:38 +0000
committerJuan A. Suarez Romero <jasuarez@igalia.com>2011-12-13 15:34:43 +0000
commit442c16a7becf8728161f6d66f1dd5c4d0cf00713 (patch)
tree37f7222247527cdbb1cc6a4b3a4134d1992659ce
parenta90b2b6ed8cb8297fddbb31ff96642f42aeed9d8 (diff)
downloadgrilo-plugins-442c16a7becf8728161f6d66f1dd5c4d0cf00713.tar.gz
shoutcast: Move SHOUTCast 2.0 API
Previous API was unavailable, so we have moved to use the new API 2.0. In order to use this plugin, a "dev-key" must be provided. See SHOUTCast webpage for more details. Signed-off-by: Juan A. Suarez Romero <jasuarez@igalia.com>
-rw-r--r--src/media/shoutcast/grl-shoutcast.c172
-rw-r--r--src/media/shoutcast/grl-shoutcast.h3
2 files changed, 130 insertions, 45 deletions
diff --git a/src/media/shoutcast/grl-shoutcast.c b/src/media/shoutcast/grl-shoutcast.c
index 57b2240..35776e2 100644
--- a/src/media/shoutcast/grl-shoutcast.c
+++ b/src/media/shoutcast/grl-shoutcast.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Igalia S.L.
+ * Copyright (C) 2010, 2011 Igalia S.L.
*
* Contact: Iago Toral Quiroga <itoral@igalia.com>
*
@@ -33,8 +33,15 @@
#include "grl-shoutcast.h"
+#define GRL_SHOUTCAST_SOURCE_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((object), \
+ GRL_SHOUTCAST_SOURCE_TYPE, \
+ GrlShoutcastSourcePriv))
+
#define EXPIRE_CACHE_TIMEOUT 300
+#define SHOUTCAST_DEV_KEY "dev-key"
+
/* --------- Logging -------- */
#define GRL_LOG_DOMAIN_DEFAULT shoutcast_log_domain
@@ -42,12 +49,13 @@ GRL_LOG_DOMAIN_STATIC(shoutcast_log_domain);
/* ------ SHOUTcast API ------ */
-#define SHOUTCAST_BASE_ENTRY "http://yp.shoutcast.com"
+#define SHOUTCAST_API_BASE_ENTRY "http://api.shoutcast.com/legacy/"
+#define SHOUTCAST_YP_BASE_ENTRY "http://yp.shoutcast.com/sbin/"
-#define SHOUTCAST_GET_GENRES SHOUTCAST_BASE_ENTRY "/sbin/newxml.phtml"
-#define SHOUTCAST_GET_RADIOS SHOUTCAST_GET_GENRES "?genre=%s&limit=%u"
-#define SHOUTCAST_SEARCH_RADIOS SHOUTCAST_GET_GENRES "?search=%s&limit=%u"
-#define SHOUTCAST_TUNE SHOUTCAST_BASE_ENTRY "/sbin/tunein-station.pls?id=%s"
+#define SHOUTCAST_GET_GENRES SHOUTCAST_API_BASE_ENTRY "genrelist?k=%s"
+#define SHOUTCAST_GET_RADIOS SHOUTCAST_API_BASE_ENTRY "genresearch?k=%s&genre=%s&limit=%u"
+#define SHOUTCAST_SEARCH_RADIOS SHOUTCAST_API_BASE_ENTRY "stationsearch?k=%s&search=%s&limit=%u"
+#define SHOUTCAST_TUNE SHOUTCAST_YP_BASE_ENTRY "tunein-station.pls?id=%s"
/* --- Plugin information --- */
@@ -57,6 +65,14 @@ GRL_LOG_DOMAIN_STATIC(shoutcast_log_domain);
#define SOURCE_NAME "SHOUTcast"
#define SOURCE_DESC "A source for browsing SHOUTcast radios"
+struct _GrlShoutcastSourcePriv {
+ gchar *dev_key;
+ GrlNetWc *wc;
+ GCancellable *cancellable;
+ gchar *cached_page;
+ gboolean cached_page_expired;
+};
+
typedef struct {
GrlMedia *media;
GrlMediaSource *source;
@@ -76,12 +92,7 @@ typedef struct {
xmlNodePtr xml_entries;
} OperationData;
-static GrlNetWc *wc = NULL;
-static GCancellable *cancellable;
-static gchar *cached_page = NULL;
-static gboolean cached_page_expired = TRUE;
-
-static GrlShoutcastSource *grl_shoutcast_source_new (void);
+static GrlShoutcastSource *grl_shoutcast_source_new (const gchar *dev_key);
gboolean grl_shoutcast_plugin_init (GrlPluginRegistry *registry,
const GrlPluginInfo *plugin,
@@ -101,7 +112,9 @@ static void grl_shoutcast_source_search (GrlMediaSource *source,
static void grl_shoutcast_source_cancel (GrlMetadataSource *source,
guint operation_id);
-static void read_url_async (const gchar *url, OperationData *op_data);
+static void read_url_async (GrlShoutcastSource *source,
+ const gchar *url,
+ OperationData *op_data);
static void grl_shoutcast_source_finalize (GObject *object);
@@ -112,15 +125,40 @@ grl_shoutcast_plugin_init (GrlPluginRegistry *registry,
const GrlPluginInfo *plugin,
GList *configs)
{
+ gchar *dev_key;
+ GrlConfig *config;
+ gint config_count;
+ GrlShoutcastSource *source;
+
GRL_LOG_DOMAIN_INIT (shoutcast_log_domain, "shoutcast");
GRL_DEBUG ("shoutcast_plugin_init");
- GrlShoutcastSource *source = grl_shoutcast_source_new ();
+ if (!configs) {
+ GRL_INFO ("Configuration not provided! Plugin not loaded");
+ return FALSE;
+ }
+
+ config_count = g_list_length (configs);
+ if (config_count > 1) {
+ GRL_INFO ("Provided %d configs, but will only use one", config_count);
+ }
+
+ config = GRL_CONFIG (configs->data);
+ dev_key = grl_config_get_string (config, SHOUTCAST_DEV_KEY);
+ if (!dev_key) {
+ GRL_INFO ("Missin API Dev Key, cannot load plugin");
+ return FALSE;
+ }
+
+ source = grl_shoutcast_source_new (dev_key);
grl_plugin_registry_register_source (registry,
plugin,
GRL_MEDIA_PLUGIN (source),
NULL);
+
+ g_free (dev_key);
+
return TRUE;
}
@@ -131,14 +169,21 @@ GRL_PLUGIN_REGISTER (grl_shoutcast_plugin_init,
/* ================== SHOUTcast GObject ================ */
static GrlShoutcastSource *
-grl_shoutcast_source_new (void)
+grl_shoutcast_source_new (const gchar *dev_key)
{
+ GrlShoutcastSource *source;
+
GRL_DEBUG ("grl_shoutcast_source_new");
- return g_object_new (GRL_SHOUTCAST_SOURCE_TYPE,
- "source-id", SOURCE_ID,
- "source-name", SOURCE_NAME,
- "source-desc", SOURCE_DESC,
- NULL);
+
+ source = g_object_new (GRL_SHOUTCAST_SOURCE_TYPE,
+ "source-id", SOURCE_ID,
+ "source-name", SOURCE_NAME,
+ "source-desc", SOURCE_DESC,
+ NULL);
+
+ source->priv->dev_key = g_strdup (dev_key);
+
+ return source;
}
static void
@@ -147,17 +192,24 @@ grl_shoutcast_source_class_init (GrlShoutcastSourceClass * klass)
GrlMediaSourceClass *source_class = GRL_MEDIA_SOURCE_CLASS (klass);
GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
source_class->metadata = grl_shoutcast_source_metadata;
source_class->browse = grl_shoutcast_source_browse;
source_class->search = grl_shoutcast_source_search;
+
metadata_class->cancel = grl_shoutcast_source_cancel;
metadata_class->supported_keys = grl_shoutcast_source_supported_keys;
+
gobject_class->finalize = grl_shoutcast_source_finalize;
+
+ g_type_class_add_private (klass, sizeof (GrlShoutcastSourcePriv));
}
static void
grl_shoutcast_source_init (GrlShoutcastSource *source)
{
+ source->priv = GRL_SHOUTCAST_SOURCE_GET_PRIVATE (source);
+ source->priv->cached_page_expired = TRUE;
}
G_DEFINE_TYPE (GrlShoutcastSource, grl_shoutcast_source, GRL_TYPE_MEDIA_SOURCE);
@@ -165,11 +217,22 @@ G_DEFINE_TYPE (GrlShoutcastSource, grl_shoutcast_source, GRL_TYPE_MEDIA_SOURCE);
static void
grl_shoutcast_source_finalize (GObject *object)
{
- if (wc && GRL_IS_NET_WC (wc))
- g_object_unref (wc);
+ GrlShoutcastSource *self = GRL_SHOUTCAST_SOURCE (object);
+
+ if (self->priv->wc && GRL_IS_NET_WC (self->priv->wc))
+ g_object_unref (self->priv->wc);
- if (cancellable && G_IS_CANCELLABLE (cancellable))
- g_cancellable_cancel (cancellable);
+ if (self->priv->cancellable && G_IS_CANCELLABLE (self->priv->cancellable))
+ g_cancellable_cancel (self->priv->cancellable);
+
+ if (self->priv->cached_page) {
+ g_free (self->priv->cached_page);
+ self->priv->cached_page = NULL;
+ }
+
+ if (self->priv->dev_key) {
+ g_free (self->priv->dev_key);
+ }
G_OBJECT_CLASS (grl_shoutcast_source_parent_class)->finalize (object);
}
@@ -469,8 +532,10 @@ xml_parse_result (const gchar *str, OperationData *op_data)
static gboolean
expire_cache (gpointer user_data)
{
+ GrlShoutcastSource *source = GRL_SHOUTCAST_SOURCE (user_data);
+
GRL_DEBUG ("Cached page expired");
- cached_page_expired = TRUE;
+ source->priv->cached_page_expired = TRUE;
return FALSE;
}
@@ -482,6 +547,7 @@ read_done_cb (GObject *source_object,
GError *error = NULL;
GError *wc_error = NULL;
OperationData *op_data = (OperationData *) user_data;
+ GrlShoutcastSource *source = GRL_SHOUTCAST_SOURCE (op_data->source);
gboolean cache;
gchar *content = NULL;
@@ -509,34 +575,39 @@ read_done_cb (GObject *source_object,
cache = op_data->cache;
xml_parse_result (content, op_data);
- if (cache && cached_page_expired) {
+ if (cache && source->priv->cached_page_expired) {
GRL_DEBUG ("Caching page");
- g_free (cached_page);
- cached_page = g_strdup (content);
- cached_page_expired = FALSE;
- g_timeout_add_seconds (EXPIRE_CACHE_TIMEOUT, expire_cache, NULL);
+ g_free (source->priv->cached_page);
+ source->priv->cached_page = g_strdup (content);
+ source->priv->cached_page_expired = FALSE;
+ g_timeout_add_seconds (EXPIRE_CACHE_TIMEOUT, expire_cache, source);
}
}
static gboolean
read_cached_page (OperationData *op_data)
{
+ gchar *cached_page = GRL_SHOUTCAST_SOURCE (op_data->source)->priv->cached_page;
xml_parse_result (cached_page, op_data);
return FALSE;
}
static void
-read_url_async (const gchar *url, OperationData *op_data)
+read_url_async (GrlShoutcastSource *source,
+ const gchar *url,
+ OperationData *op_data)
{
- if (op_data->cache && !cached_page_expired) {
+ if (op_data->cache && !source->priv->cached_page_expired) {
GRL_DEBUG ("Using cached page");
g_idle_add ((GSourceFunc) read_cached_page, op_data);
} else {
- if (!wc)
- wc = grl_net_wc_new ();
+ if (!source->priv->wc)
+ source->priv->wc = grl_net_wc_new ();
- cancellable = g_cancellable_new ();
- grl_net_wc_request_async (wc, url, cancellable, read_done_cb, op_data);
+ source->priv->cancellable = g_cancellable_new ();
+ grl_net_wc_request_async (source->priv->wc, url,
+ source->priv->cancellable,
+ read_done_cb, op_data);
}
}
@@ -566,6 +637,7 @@ grl_shoutcast_source_metadata (GrlMediaSource *source,
gchar **id_tokens;
gchar *url = NULL;
OperationData *data = NULL;
+ GrlShoutcastSource *shoutcast_source = GRL_SHOUTCAST_SOURCE (source);
/* Unfortunately, shoutcast does not have an API to get information about a
station. Thus, steps done to obtain the Content must be repeated. For
@@ -598,24 +670,27 @@ grl_shoutcast_source_metadata (GrlMediaSource *source,
/* Check if result is from a previous search */
if (id_tokens[0][0] == '?') {
url = g_strdup_printf (SHOUTCAST_SEARCH_RADIOS,
+ shoutcast_source->priv->dev_key,
id_tokens[0]+1,
G_MAXINT);
} else {
url = g_strdup_printf (SHOUTCAST_GET_RADIOS,
+ shoutcast_source->priv->dev_key,
id_tokens[0],
G_MAXINT);
}
} else {
data->filter_entry = g_strdup (id_tokens[0]);
data->cache = TRUE;
- url = g_strdup (SHOUTCAST_GET_GENRES);
+ url = g_strdup_printf (SHOUTCAST_GET_GENRES,
+ shoutcast_source->priv->dev_key);
}
g_strfreev (id_tokens);
}
if (url) {
- read_url_async (url, data);
+ read_url_async (shoutcast_source, url, data);
g_free (url);
} else {
ms->callback (ms->source, ms->metadata_id, ms->media, ms->user_data, NULL);
@@ -629,6 +704,7 @@ grl_shoutcast_source_browse (GrlMediaSource *source,
OperationData *data;
const gchar *container_id;
gchar *url;
+ GrlShoutcastSource *shoutcast_source = GRL_SHOUTCAST_SOURCE (source);
GRL_DEBUG ("grl_shoutcast_source_browse");
@@ -646,9 +722,11 @@ grl_shoutcast_source_browse (GrlMediaSource *source,
/* If it's root category send list of genres; else send list of radios */
if (!container_id) {
data->cache = TRUE;
- url = g_strdup (SHOUTCAST_GET_GENRES);
+ url = g_strdup_printf (SHOUTCAST_GET_GENRES,
+ shoutcast_source->priv->dev_key);
} else {
url = g_strdup_printf (SHOUTCAST_GET_RADIOS,
+ shoutcast_source->priv->dev_key,
container_id,
bs->skip + bs->count);
data->genre = g_strdup (container_id);
@@ -656,7 +734,7 @@ grl_shoutcast_source_browse (GrlMediaSource *source,
grl_operation_set_data (bs->browse_id, data);
- read_url_async (url, data);
+ read_url_async (shoutcast_source, url, data);
g_free (url);
}
@@ -668,6 +746,7 @@ grl_shoutcast_source_search (GrlMediaSource *source,
GError *error;
OperationData *data;
gchar *url;
+ GrlShoutcastSource *shoutcast_source = GRL_SHOUTCAST_SOURCE (source);
/* Check if there is text to search */
if (!ss->text || ss->text[0] == '\0') {
@@ -697,10 +776,11 @@ grl_shoutcast_source_search (GrlMediaSource *source,
grl_operation_set_data (ss->search_id, data);
url = g_strdup_printf (SHOUTCAST_SEARCH_RADIOS,
+ shoutcast_source->priv->dev_key,
ss->text,
ss->skip + ss->count);
- read_url_async (url, data);
+ read_url_async (GRL_SHOUTCAST_SOURCE (source), url, data);
g_free (url);
}
@@ -709,12 +789,16 @@ static void
grl_shoutcast_source_cancel (GrlMetadataSource *source, guint operation_id)
{
OperationData *op_data;
+ GrlShoutcastSourcePriv *priv;
GRL_DEBUG ("grl_shoutcast_source_cancel");
- if (cancellable && G_IS_CANCELLABLE (cancellable))
- g_cancellable_cancel (cancellable);
- cancellable = NULL;
+ priv = GRL_SHOUTCAST_SOURCE_GET_PRIVATE (source);
+
+ if (priv->cancellable && G_IS_CANCELLABLE (priv->cancellable)) {
+ g_cancellable_cancel (priv->cancellable);
+ }
+ priv->cancellable = NULL;
op_data = (OperationData *) grl_operation_get_data (operation_id);
diff --git a/src/media/shoutcast/grl-shoutcast.h b/src/media/shoutcast/grl-shoutcast.h
index 741fac0..0672fa0 100644
--- a/src/media/shoutcast/grl-shoutcast.h
+++ b/src/media/shoutcast/grl-shoutcast.h
@@ -54,11 +54,12 @@
GrlShoutcastSourceClass))
typedef struct _GrlShoutcastSource GrlShoutcastSource;
+typedef struct _GrlShoutcastSourcePriv GrlShoutcastSourcePriv;
struct _GrlShoutcastSource {
GrlMediaSource parent;
-
+ GrlShoutcastSourcePriv *priv;
};
typedef struct _GrlShoutcastSourceClass GrlShoutcastSourceClass;