[rhythmbox] audioscrobbler: add class for requesting/parsing user's profile data
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] audioscrobbler: add class for requesting/parsing user's profile data
- Date: Tue, 21 Sep 2010 10:49:05 +0000 (UTC)
commit 349022a70e17864b314a64c6ebaafdfd90eff23b
Author: Jamie Nicol <jamie thenicols net>
Date: Wed Jun 23 15:03:37 2010 +0100
audioscrobbler: add class for requesting/parsing user's profile data
RBAudioscrobblerUser sends requests and parses responses for a user's
info and listening statistics such as top artists and recently played
tracks.
configure.ac | 30 +
plugins/Makefile.am | 4 +-
plugins/audioscrobbler/Makefile.am | 4 +
plugins/audioscrobbler/rb-audioscrobbler-user.c | 1353 +++++++++++++++++++++++
plugins/audioscrobbler/rb-audioscrobbler-user.h | 104 ++
5 files changed, 1494 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d80adf7..8fd1b6b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -810,6 +810,30 @@ AC_SUBST(DMAPSHARING_CFLAGS)
AC_SUBST(DMAPSHARING_LIBS)
dnl ================================================================
+dnl Dependencies for Last.fm plugin
+dnl ================================================================
+AC_ARG_ENABLE(lastfm,
+ AC_HELP_STRING([--disable-lastfm],
+ [Disable Last.fm support]),,
+ enable_lastfm=auto)
+if test "x$enable_lastfm" != "xno"; then
+ PKG_CHECK_MODULES(JSON_GLIB, json-glib-1.0,
+ have_json_glib=yes,
+ have_json_glib=no)
+ if test "x$have_json_glib" = "xno" -a "x$enable_lastfm" = "xyes"; then
+ AC_MSG_ERROR([Last.fm support explicitly requested, but json-glib couldn't be found])
+ fi
+ if test x"$have_json_glib" = "xyes"; then
+ AC_DEFINE(HAVE_JSON_GLIB, 1, [Define if json-glib support is enabled])
+ fi
+fi
+
+AM_CONDITIONAL(ENABLE_LASTFM, test x"$have_json_glib" = "xyes")
+
+AC_SUBST(JSON_GLIB_CFLAGS)
+AC_SUBST(JSON_GLIB_LIBS)
+
+dnl ================================================================
dnl end-game
dnl ================================================================
@@ -998,4 +1022,10 @@ else
AC_MSG_NOTICE([ iTunes detection browser plugin (for podcasts) disabled])
fi
+if test "x$have_json_glib" = xyes; then
+ AC_MSG_NOTICE([** Last.fm support enabled])
+else
+ AC_MSG_NOTICE([ Last.fm support disabled])
+fi
+
AC_MSG_NOTICE([End options])
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 45ee2f8..3d92e45 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,6 +1,5 @@
SUBDIRS = \
audiocd \
- audioscrobbler \
generic-player \
iradio \
mmkeys \
@@ -64,3 +63,6 @@ SUBDIRS += \
mpris
endif
+if ENABLE_LASTFM
+SUBDIRS += audioscrobbler
+endif
diff --git a/plugins/audioscrobbler/Makefile.am b/plugins/audioscrobbler/Makefile.am
index bd1b757..9c99c61 100644
--- a/plugins/audioscrobbler/Makefile.am
+++ b/plugins/audioscrobbler/Makefile.am
@@ -13,6 +13,8 @@ libaudioscrobbler_la_SOURCES = \
rb-audioscrobbler-account.c \
rb-audioscrobbler-service.h \
rb-audioscrobbler-service.c \
+ rb-audioscrobbler-user.h \
+ rb-audioscrobbler-user.c \
rb-audioscrobbler.c \
rb-audioscrobbler.h \
rb-lastfm-source.c \
@@ -25,6 +27,7 @@ libaudioscrobbler_la_LIBTOOLFLAGS = --tag=disable-static
libaudioscrobbler_la_LIBADD = \
$(top_builddir)/shell/librhythmbox-core.la \
$(TOTEM_PLPARSER_LIBS) \
+ $(JSON_GLIB_LIBS) \
$(RHYTHMBOX_LIBS)
INCLUDES = \
@@ -45,6 +48,7 @@ INCLUDES = \
-DSHARE_DIR=\"$(pkgdatadir)\" \
-DDATADIR=\""$(datadir)"\" \
$(TOTEM_PLPARSER_CFLAGS) \
+ $(JSON_GLIB_CFLAGS) \
$(RHYTHMBOX_CFLAGS) \
-D_XOPEN_SOURCE -D_BSD_SOURCE
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-user.c b/plugins/audioscrobbler/rb-audioscrobbler-user.c
new file mode 100644
index 0000000..497d2d1
--- /dev/null
+++ b/plugins/audioscrobbler/rb-audioscrobbler-user.c
@@ -0,0 +1,1353 @@
+/*
+ * rb-audioscrobbler-user.c
+ *
+ * Copyright (C) 2010 Jamie Nicol <jamie thenicols net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <libsoup/soup.h>
+#include <libsoup/soup-gnome.h>
+#include <json-glib/json-glib.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "rb-audioscrobbler-user.h"
+#include "rb-debug.h"
+#include "rb-file-helpers.h"
+
+static RBAudioscrobblerUserData *
+rb_audioscrobbler_user_data_copy (RBAudioscrobblerUserData *data)
+{
+ RBAudioscrobblerUserData *d = g_slice_new0 (RBAudioscrobblerUserData);
+
+ switch (d->type) {
+ case RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO:
+ d->user_info.username = g_strdup (data->user_info.username);
+ d->user_info.real_name = g_strdup (data->user_info.real_name);
+ d->user_info.url = g_strdup (data->user_info.url);
+ if (data->user_info.image != NULL) {
+ d->user_info.image = g_object_ref (data->user_info.image);
+ }
+ break;
+ case RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK:
+ d->track.title = g_strdup (data->track.title);
+ d->track.artist = g_strdup (data->track.artist);
+ d->track.url = g_strdup (data->track.url);
+ if (data->track.image != NULL) {
+ d->track.image = g_object_ref (data->track.image);
+ }
+ break;
+ case RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST:
+ d->artist.name = g_strdup (data->artist.name);
+ d->artist.url = g_strdup (data->artist.url);
+ if (data->artist.image != NULL) {
+ d->artist.image = g_object_ref (data->artist.image);
+ }
+ break;
+ }
+
+ return d;
+}
+
+static void
+rb_audioscrobbler_user_data_free (RBAudioscrobblerUserData *data)
+{
+ switch (data->type) {
+ case RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO:
+ g_free (data->user_info.username);
+ g_free (data->user_info.real_name);
+ g_free (data->user_info.url);
+ if (data->user_info.image != NULL) {
+ g_object_unref (data->user_info.image);
+ }
+ break;
+ case RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK:
+ g_free (data->track.title);
+ g_free (data->track.artist);
+ g_free (data->track.url);
+ if (data->track.image != NULL) {
+ g_object_unref (data->track.image);
+ }
+ break;
+ case RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST:
+ g_free (data->artist.name);
+ g_free (data->artist.url);
+ if (data->track.image != NULL) {
+ g_object_unref (data->artist.image);
+ }
+ break;
+ }
+
+ g_slice_free (RBAudioscrobblerUserData, data);
+}
+
+GType
+rb_audioscrobbler_user_data_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (type == 0)) {
+ type = g_boxed_type_register_static ("RBAudioscrobblerUserData",
+ (GBoxedCopyFunc)rb_audioscrobbler_user_data_copy,
+ (GBoxedFreeFunc)rb_audioscrobbler_user_data_free);
+ }
+
+ return type;
+}
+
+struct _RBAudioscrobblerUserPrivate {
+ RBAudioscrobblerService *service;
+ char *username;
+ char *session_key;
+
+ SoupSession *soup_session;
+
+ RBAudioscrobblerUserData *user_info;
+ GPtrArray *recent_tracks;
+ GPtrArray *top_tracks;
+ GPtrArray *loved_tracks;
+ GPtrArray *top_artists;
+ GPtrArray *recommended_artists;
+};
+
+#define RB_AUDIOSCROBBLER_USER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_AUDIOSCROBBLER_USER, RBAudioscrobblerUserPrivate))
+
+static void rb_audioscrobbler_user_class_init (RBAudioscrobblerUserClass *klass);
+static void rb_audioscrobbler_user_init (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_constructed (GObject *object);
+static void rb_audioscrobbler_user_dispose (GObject* object);
+static void rb_audioscrobbler_user_finalize (GObject *object);
+static void rb_audioscrobbler_user_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void rb_audioscrobbler_user_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void rb_audioscrobbler_user_load_from_cache (RBAudioscrobblerUser *user);
+
+
+static void rb_audioscrobbler_user_save_response_to_cache (RBAudioscrobblerUser *user,
+ const char *request_name,
+ const char *data);
+
+static void rb_audioscrobbler_user_load_cached_user_info (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_request_user_info (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_user_info_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+static RBAudioscrobblerUserData * rb_audioscrobbler_user_parse_user_info (RBAudioscrobblerUser *user,
+ const char *data);
+
+static void rb_audioscrobbler_user_load_cached_recent_tracks (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_request_recent_tracks (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_recent_tracks_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+static GPtrArray * rb_audioscrobbler_user_parse_recent_tracks (RBAudioscrobblerUser *user,
+ const char *data);
+
+static void rb_audioscrobbler_user_load_cached_top_tracks (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_request_top_tracks (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_top_tracks_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+static GPtrArray * rb_audioscrobbler_user_parse_top_tracks (RBAudioscrobblerUser *user,
+ const char *data);
+
+static void rb_audioscrobbler_user_load_cached_loved_tracks (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_request_loved_tracks (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_loved_tracks_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+static GPtrArray * rb_audioscrobbler_user_parse_loved_tracks (RBAudioscrobblerUser *user,
+ const char *data);
+
+static void rb_audioscrobbler_user_load_cached_top_artists (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_request_top_artists (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_top_artists_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+static GPtrArray * rb_audioscrobbler_user_parse_top_artists (RBAudioscrobblerUser *user,
+ const char *data);
+
+static void rb_audioscrobbler_user_load_cached_recommended_artists (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_request_recommended_artists (RBAudioscrobblerUser *user);
+static void rb_audioscrobbler_user_recommended_artists_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data);
+static GPtrArray * rb_audioscrobbler_user_parse_recommended_artists (RBAudioscrobblerUser *user,
+ const char *data);
+enum {
+ PROP_0,
+ PROP_SERVICE
+};
+
+enum {
+ USER_INFO_UPDATED,
+ RECENT_TRACKS_UPDATED,
+ TOP_TRACKS_UPDATED,
+ LOVED_TRACKS_UPDATED,
+ TOP_ARTISTS_UPDATED,
+ RECOMMENDED_ARTISTS_UPDATED,
+ LAST_SIGNAL
+};
+
+static guint rb_audioscrobbler_user_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (RBAudioscrobblerUser, rb_audioscrobbler_user, G_TYPE_OBJECT)
+
+RBAudioscrobblerUser *
+rb_audioscrobbler_user_new (RBAudioscrobblerService *service)
+{
+ return g_object_new (RB_TYPE_AUDIOSCROBBLER_USER,
+ "service", service,
+ NULL);
+}
+
+static void
+rb_audioscrobbler_user_class_init (RBAudioscrobblerUserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = rb_audioscrobbler_user_constructed;
+ object_class->dispose = rb_audioscrobbler_user_dispose;
+ object_class->finalize = rb_audioscrobbler_user_finalize;
+ object_class->get_property = rb_audioscrobbler_user_get_property;
+ object_class->set_property = rb_audioscrobbler_user_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_SERVICE,
+ g_param_spec_object ("service",
+ "Service",
+ "Audioscrobbler service that this should use for requests",
+ RB_TYPE_AUDIOSCROBBLER_SERVICE,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+
+ rb_audioscrobbler_user_signals[USER_INFO_UPDATED] =
+ g_signal_new ("user-info-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ RB_TYPE_AUDIOSCROBBLER_USER_DATA);
+
+ rb_audioscrobbler_user_signals[RECENT_TRACKS_UPDATED] =
+ g_signal_new ("recent-tracks-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_PTR_ARRAY);
+
+ rb_audioscrobbler_user_signals[TOP_TRACKS_UPDATED] =
+ g_signal_new ("top-tracks-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_PTR_ARRAY);
+
+ rb_audioscrobbler_user_signals[LOVED_TRACKS_UPDATED] =
+ g_signal_new ("loved-tracks-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_PTR_ARRAY);
+
+ rb_audioscrobbler_user_signals[TOP_ARTISTS_UPDATED] =
+ g_signal_new ("top-artists-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_PTR_ARRAY);
+
+ rb_audioscrobbler_user_signals[RECOMMENDED_ARTISTS_UPDATED] =
+ g_signal_new ("recommended-artists-updated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_PTR_ARRAY);
+
+ g_type_class_add_private (klass, sizeof (RBAudioscrobblerUserPrivate));
+}
+
+static void
+rb_audioscrobbler_user_init (RBAudioscrobblerUser *user)
+{
+ user->priv = RB_AUDIOSCROBBLER_USER_GET_PRIVATE (user);
+
+ user->priv->soup_session =
+ soup_session_async_new_with_options (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
+ SOUP_TYPE_GNOME_FEATURES_2_26,
+ NULL);
+}
+
+static void
+rb_audioscrobbler_user_constructed (GObject *object)
+{
+}
+
+static void
+rb_audioscrobbler_user_dispose (GObject* object)
+{
+ RBAudioscrobblerUser *user = RB_AUDIOSCROBBLER_USER (object);
+
+ if (user->priv->service != NULL) {
+ g_object_unref (user->priv->service);
+ user->priv->service = NULL;
+ }
+
+ if (user->priv->soup_session != NULL) {
+ soup_session_abort (user->priv->soup_session);
+ g_object_unref (user->priv->soup_session);
+ user->priv->soup_session = NULL;
+ }
+
+ if (user->priv->user_info != NULL) {
+ g_boxed_free (RB_TYPE_AUDIOSCROBBLER_USER_DATA, user->priv->user_info);
+ user->priv->user_info = NULL;
+ }
+
+ if (user->priv->recent_tracks != NULL) {
+ g_ptr_array_unref (user->priv->recent_tracks);
+ user->priv->recent_tracks = NULL;
+ }
+
+ if (user->priv->top_tracks != NULL) {
+ g_ptr_array_unref (user->priv->top_tracks);
+ user->priv->top_tracks = NULL;
+ }
+
+ if (user->priv->loved_tracks != NULL) {
+ g_ptr_array_unref (user->priv->loved_tracks);
+ user->priv->loved_tracks = NULL;
+ }
+
+ if (user->priv->top_artists != NULL) {
+ g_ptr_array_unref (user->priv->top_artists);
+ user->priv->top_artists = NULL;
+ }
+
+ if (user->priv->recommended_artists != NULL) {
+ g_ptr_array_unref (user->priv->recommended_artists);
+ user->priv->recommended_artists = NULL;
+ }
+}
+
+static void
+rb_audioscrobbler_user_finalize (GObject *object)
+{
+ RBAudioscrobblerUser *user = RB_AUDIOSCROBBLER_USER (object);
+
+ g_free (user->priv->username);
+ g_free (user->priv->session_key);
+}
+
+static void
+rb_audioscrobbler_user_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rb_audioscrobbler_user_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RBAudioscrobblerUser *user = RB_AUDIOSCROBBLER_USER (object);
+ switch (prop_id) {
+ case PROP_SERVICE:
+ user->priv->service = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gchar *
+mkmd5 (char *string)
+{
+ GChecksum *checksum;
+ gchar *md5_result;
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (guchar *)string, -1);
+
+ md5_result = g_strdup (g_checksum_get_string (checksum));
+ g_checksum_free (checksum);
+
+ return md5_result;
+}
+
+static char *
+rb_audioscrobbler_user_calculate_cached_response_path (RBAudioscrobblerUser *user, const char *request_name)
+{
+ const char *rb_cache_dir;
+ rb_cache_dir = rb_user_cache_dir ();
+
+ return g_build_filename (rb_cache_dir,
+ "audioscrobbler",
+ rb_audioscrobbler_service_get_name (user->priv->service),
+ "ws-responses",
+ user->priv->username,
+ request_name,
+ NULL);
+}
+
+static void
+rb_audioscrobbler_user_save_response_to_cache (RBAudioscrobblerUser *user, const char *request_name, const char *data)
+{
+ char *filename;
+ char *file_dir;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, request_name);
+ file_dir = g_path_get_dirname (filename);
+
+ g_mkdir_with_parents (file_dir, 0700);
+ if (g_file_set_contents (filename, data, -1, NULL)) {
+ rb_debug ("saved %s to cache", request_name);
+ } else {
+ rb_debug ("error saving %s to cache", request_name);
+ }
+
+ g_free (filename);
+ g_free (file_dir);
+}
+
+/*static char *
+rb_audioscrobbler_user_calculate_cached_image_path (RBAudioscrobblerUser *user, RBAudioscrobblerUserData *data)
+{
+ const char *rb_cache_dir;
+ char *cache_dir;
+ char *image_path = NULL;
+
+ rb_cache_dir = rb_user_cache_dir ();
+ cache_dir = g_build_filename (rb_cache_dir,
+ "audioscrobbler",
+ rb_audioscrobbler_service_get_name (user->priv->service),
+ "images",
+ NULL);
+
+ if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO) {
+ image_path = g_build_filename (cache_dir,
+ "users",
+ data->user_info.username,
+ NULL);
+
+ } else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK) {
+ char *filename = g_strdup_printf ("%s - %s",
+ data->track.artist,
+ data->track.title);
+ image_path = g_build_filename (cache_dir,
+ "tracks",
+ filename,
+ NULL);
+ g_free (filename);
+
+ } else if (data->type == RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST) {
+ image_path = g_build_filename (cache_dir,
+ "artists",
+ data->artist.name,
+ NULL);
+ }
+
+ g_free (cache_dir);
+ return image_path;
+}*/
+
+void
+rb_audioscrobbler_user_set_authentication_details (RBAudioscrobblerUser *user,
+ const char *username,
+ const char *session_key)
+{
+ g_free (user->priv->username);
+ user->priv->username = g_strdup (username);
+
+ g_free (user->priv->session_key);
+ user->priv->session_key = g_strdup (session_key);
+
+ /* cancel pending requests */
+ soup_session_abort (user->priv->soup_session);
+
+ /* load new user from cache (or set to NULL) */
+ rb_audioscrobbler_user_load_from_cache (user);
+
+ /* request new info */
+ //rb_audioscrobbler_user_update (user);
+}
+
+void
+rb_audioscrobbler_user_update (RBAudioscrobblerUser *user)
+{
+ if (user->priv->username != NULL) {
+ rb_audioscrobbler_user_request_user_info (user);
+ rb_audioscrobbler_user_request_recent_tracks (user);
+ rb_audioscrobbler_user_request_top_tracks (user);
+ rb_audioscrobbler_user_request_loved_tracks (user);
+ rb_audioscrobbler_user_request_top_artists (user);
+ rb_audioscrobbler_user_request_recommended_artists (user);
+ }
+}
+
+static void
+rb_audioscrobbler_user_load_from_cache (RBAudioscrobblerUser *user)
+{
+ /* delete old data */
+ if (user->priv->user_info != NULL) {
+ g_boxed_free (RB_TYPE_AUDIOSCROBBLER_USER_DATA, user->priv->user_info);
+ user->priv->user_info = NULL;
+ }
+
+ if (user->priv->recent_tracks != NULL) {
+ g_ptr_array_unref (user->priv->recent_tracks);
+ user->priv->recent_tracks = NULL;
+ }
+
+ if (user->priv->top_tracks != NULL) {
+ g_ptr_array_unref (user->priv->top_tracks);
+ user->priv->top_tracks = NULL;
+ }
+
+ if (user->priv->loved_tracks != NULL) {
+ g_ptr_array_unref (user->priv->loved_tracks);
+ user->priv->loved_tracks = NULL;
+ }
+
+ if (user->priv->top_artists != NULL) {
+ g_ptr_array_unref (user->priv->top_artists);
+ user->priv->top_artists = NULL;
+ }
+
+ if (user->priv->recommended_artists != NULL) {
+ g_ptr_array_unref (user->priv->recommended_artists);
+ user->priv->recommended_artists = NULL;
+ }
+
+ /* if a username is set then attempt to load cached data */
+ if (user->priv->username != NULL) {
+ rb_audioscrobbler_user_load_cached_user_info (user);
+ rb_audioscrobbler_user_load_cached_recent_tracks (user);
+ rb_audioscrobbler_user_load_cached_top_tracks (user);
+ rb_audioscrobbler_user_load_cached_loved_tracks (user);
+ rb_audioscrobbler_user_load_cached_top_artists (user);
+ rb_audioscrobbler_user_load_cached_recommended_artists (user);
+ }
+}
+
+/* user info */
+static void
+rb_audioscrobbler_user_load_cached_user_info (RBAudioscrobblerUser *user)
+{
+ char *filename;
+ char *data;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, "user_info");
+
+ /* delete old data */
+ if (user->priv->user_info != NULL) {
+ g_boxed_free (RB_TYPE_AUDIOSCROBBLER_USER_DATA, user->priv->user_info);
+ user->priv->user_info = NULL;
+ }
+
+ /* load cached data if it exists */
+ if (g_file_get_contents (filename, &data, NULL, NULL) == TRUE) {
+ rb_debug ("loading cached user_info");
+ user->priv->user_info = rb_audioscrobbler_user_parse_user_info (user, data);
+ }
+
+ /* emit updated signal */
+ g_signal_emit (user, rb_audioscrobbler_user_signals[USER_INFO_UPDATED],
+ 0, user->priv->user_info);
+
+ g_free (filename);
+ g_free (data);
+}
+
+static void
+rb_audioscrobbler_user_request_user_info (RBAudioscrobblerUser *user)
+{
+ char *msg_url;
+ SoupMessage *msg;
+
+ rb_debug ("requesting user info");
+
+ msg_url = g_strdup_printf ("%s?method=user.getInfo&user=%s&api_key=%s&format=json",
+ rb_audioscrobbler_service_get_api_url (user->priv->service),
+ user->priv->username,
+ rb_audioscrobbler_service_get_api_key (user->priv->service));
+
+ msg = soup_message_new ("GET", msg_url);
+ soup_session_queue_message (user->priv->soup_session,
+ msg,
+ rb_audioscrobbler_user_user_info_response_cb,
+ user);
+
+ g_free (msg_url);
+}
+
+static void
+rb_audioscrobbler_user_user_info_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ RBAudioscrobblerUser *user;
+ RBAudioscrobblerUserData *user_info;
+
+ user = RB_AUDIOSCROBBLER_USER (user_data);
+ user_info = rb_audioscrobbler_user_parse_user_info (user, msg->response_body->data);
+
+ if (user_info != NULL) {
+ rb_debug ("user info request was successful");
+
+ if (user->priv->user_info != NULL) {
+ g_boxed_free (RB_TYPE_AUDIOSCROBBLER_USER_DATA, user->priv->user_info);
+ }
+ user->priv->user_info = user_info;
+
+ rb_audioscrobbler_user_save_response_to_cache (user, "user_info", msg->response_body->data);
+
+ g_signal_emit (user, rb_audioscrobbler_user_signals[USER_INFO_UPDATED],
+ 0, user->priv->user_info);
+ } else {
+ rb_debug ("invalid response from user info request");
+ }
+ } else {
+ rb_debug ("user info request responded with error");
+ }
+}
+
+static RBAudioscrobblerUserData *
+rb_audioscrobbler_user_parse_user_info (RBAudioscrobblerUser *user, const char *data)
+{
+ RBAudioscrobblerUserData *user_info;
+ JsonParser *parser;
+ JsonObject *root_object;
+
+ user_info = NULL;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, -1, NULL);
+ root_object = json_node_get_object (json_parser_get_root (parser));
+
+ if (json_object_has_member (root_object, "user")) {
+ JsonObject *user_object;
+
+ user_info = g_slice_new0 (RBAudioscrobblerUserData);
+ user_object = json_object_get_object_member (root_object, "user");
+
+ user_info->user_info.username = g_strdup (json_object_get_string_member (user_object, "name"));
+ user_info->user_info.real_name = g_strdup (json_object_get_string_member (user_object, "realname"));
+ user_info->user_info.url = g_strdup (json_object_get_string_member (user_object, "url"));
+ }
+
+ g_object_unref (parser);
+
+ return user_info;
+}
+
+/* recent tracks */
+static void
+rb_audioscrobbler_user_load_cached_recent_tracks (RBAudioscrobblerUser *user)
+{
+ char *filename;
+ char *data;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, "recent_tracks");
+
+ /* delete old data */
+ if (user->priv->recent_tracks != NULL) {
+ g_ptr_array_unref (user->priv->recent_tracks);
+ user->priv->recent_tracks = NULL;
+ }
+
+ /* load cached data if it exists */
+ if (g_file_get_contents (filename, &data, NULL, NULL) == TRUE) {
+ rb_debug ("loading cached recent tracks");
+ user->priv->recent_tracks = rb_audioscrobbler_user_parse_recent_tracks (user, data);
+ }
+
+ /* emit updated signal */
+ g_signal_emit (user, rb_audioscrobbler_user_signals[RECENT_TRACKS_UPDATED],
+ 0, user->priv->recent_tracks);
+
+ g_free (filename);
+ g_free (data);
+}
+
+static void
+rb_audioscrobbler_user_request_recent_tracks (RBAudioscrobblerUser *user)
+{
+ char *msg_url;
+ SoupMessage *msg;
+
+ rb_debug ("requesting recent tracks");
+
+ msg_url = g_strdup_printf ("%s?method=user.getRecentTracks&user=%s&api_key=%s&format=json",
+ rb_audioscrobbler_service_get_api_url (user->priv->service),
+ user->priv->username,
+ rb_audioscrobbler_service_get_api_key (user->priv->service));
+
+ msg = soup_message_new ("GET", msg_url);
+ soup_session_queue_message (user->priv->soup_session,
+ msg,
+ rb_audioscrobbler_user_recent_tracks_response_cb,
+ user);
+
+ g_free (msg_url);
+}
+
+static void
+rb_audioscrobbler_user_recent_tracks_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ RBAudioscrobblerUser *user;
+ GPtrArray *recent_tracks;
+
+ user = RB_AUDIOSCROBBLER_USER (user_data);
+ recent_tracks = rb_audioscrobbler_user_parse_recent_tracks (user, msg->response_body->data);
+
+ if (recent_tracks != NULL) {
+ rb_debug ("recent tracks request was successful");
+
+ if (user->priv->recent_tracks != NULL) {
+ g_ptr_array_unref (user->priv->recent_tracks);
+ }
+ user->priv->recent_tracks = recent_tracks;
+
+ rb_audioscrobbler_user_save_response_to_cache (user, "recent_tracks", msg->response_body->data);
+
+ g_signal_emit (user, rb_audioscrobbler_user_signals[RECENT_TRACKS_UPDATED],
+ 0, user->priv->recent_tracks);
+ } else {
+ rb_debug ("invalid response from recent tracks request");
+ }
+ } else {
+ rb_debug ("recent tracks request responded with error");
+ }
+}
+
+static GPtrArray *
+rb_audioscrobbler_user_parse_recent_tracks (RBAudioscrobblerUser *user, const char *data)
+{
+ GPtrArray *recent_tracks;
+ JsonParser *parser;
+ JsonObject *root_object;
+ JsonObject *recent_tracks_object;
+
+ recent_tracks = NULL;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, -1, NULL);
+ root_object = json_node_get_object (json_parser_get_root (parser));
+ recent_tracks_object = json_object_get_object_member (root_object, "recenttracks");
+
+ if (json_object_has_member (recent_tracks_object, "track") == TRUE) {
+ JsonArray *track_array;
+ int i;
+
+ recent_tracks = g_ptr_array_new_with_free_func ((GDestroyNotify)rb_audioscrobbler_user_data_free);
+
+ track_array = json_object_get_array_member (recent_tracks_object, "track");
+ for (i = 0; i < json_array_get_length (track_array); i++) {
+ JsonObject *track_object;
+ JsonObject *artist_object;
+ RBAudioscrobblerUserData *track;
+
+ track_object = json_array_get_object_element (track_array, i);
+
+ track = g_slice_new0 (RBAudioscrobblerUserData);
+ track->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK;
+ track->track.title = g_strdup (json_object_get_string_member (track_object, "name"));
+ artist_object = json_object_get_object_member (track_object, "artist");
+ track->track.artist = g_strdup (json_object_get_string_member (artist_object, "#text"));
+ track->track.url = g_strdup (json_object_get_string_member (track_object, "url"));
+
+ g_ptr_array_add (recent_tracks, track);
+ }
+ }
+
+ g_object_unref (parser);
+
+ return recent_tracks;
+}
+
+/* top tracks */
+static void
+rb_audioscrobbler_user_load_cached_top_tracks (RBAudioscrobblerUser *user)
+{
+ char *filename;
+ char *data;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, "top_tracks");
+
+ /* delete old data */
+ if (user->priv->top_tracks != NULL) {
+ g_ptr_array_unref (user->priv->top_tracks);
+ user->priv->top_tracks = NULL;
+ }
+
+ /* load cached data if it exists */
+ if (g_file_get_contents (filename, &data, NULL, NULL) == TRUE) {
+ rb_debug ("loading cached top tracks");
+ user->priv->top_tracks = rb_audioscrobbler_user_parse_top_tracks (user, data);
+ }
+
+ /* emit updated signal */
+ g_signal_emit (user, rb_audioscrobbler_user_signals[TOP_TRACKS_UPDATED],
+ 0, user->priv->top_tracks);
+
+ g_free (filename);
+ g_free (data);
+}
+
+static void
+rb_audioscrobbler_user_request_top_tracks (RBAudioscrobblerUser *user)
+{
+ char *msg_url;
+ SoupMessage *msg;
+
+ rb_debug ("requesting top tracks");
+
+ msg_url = g_strdup_printf ("%s?method=user.getTopTracks&user=%s&api_key=%s&format=json",
+ rb_audioscrobbler_service_get_api_url (user->priv->service),
+ user->priv->username,
+ rb_audioscrobbler_service_get_api_key (user->priv->service));
+
+ msg = soup_message_new ("GET", msg_url);
+ soup_session_queue_message (user->priv->soup_session,
+ msg,
+ rb_audioscrobbler_user_top_tracks_response_cb,
+ user);
+
+ g_free (msg_url);
+}
+
+static void
+rb_audioscrobbler_user_top_tracks_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ RBAudioscrobblerUser *user;
+ GPtrArray *top_tracks;
+
+ user = RB_AUDIOSCROBBLER_USER (user_data);
+ top_tracks = rb_audioscrobbler_user_parse_top_tracks (user, msg->response_body->data);
+
+ if (top_tracks != NULL) {
+ rb_debug ("top tracks request was successful");
+
+ if (user->priv->top_tracks != NULL) {
+ g_ptr_array_unref (user->priv->top_tracks);
+ }
+ user->priv->top_tracks = top_tracks;
+
+ rb_audioscrobbler_user_save_response_to_cache (user, "top_tracks", msg->response_body->data);
+
+ g_signal_emit (user, rb_audioscrobbler_user_signals[TOP_TRACKS_UPDATED],
+ 0, user->priv->top_tracks);
+ } else {
+ rb_debug ("invalid response from top tracks request");
+ }
+ } else {
+ rb_debug ("top tracks request responded with error");
+ }
+}
+
+static GPtrArray *
+rb_audioscrobbler_user_parse_top_tracks (RBAudioscrobblerUser *user, const char *data)
+{
+ GPtrArray *top_tracks;
+ JsonParser *parser;
+ JsonObject *root_object;
+ JsonObject *top_tracks_object;
+
+ top_tracks = NULL;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, -1, NULL);
+ root_object = json_node_get_object (json_parser_get_root (parser));
+ top_tracks_object = json_object_get_object_member (root_object, "toptracks");
+
+ if (json_object_has_member (top_tracks_object, "track") == TRUE) {
+ JsonArray *track_array;
+ int i;
+
+ top_tracks = g_ptr_array_new_with_free_func ((GDestroyNotify)rb_audioscrobbler_user_data_free);
+
+ track_array = json_object_get_array_member (top_tracks_object, "track");
+ for (i = 0; i < json_array_get_length (track_array); i++) {
+ JsonObject *track_object;
+ JsonObject *artist_object;
+ RBAudioscrobblerUserData *track;
+
+ track_object = json_array_get_object_element (track_array, i);
+
+ track = g_slice_new0 (RBAudioscrobblerUserData);
+ track->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK;
+ track->track.title = g_strdup (json_object_get_string_member (track_object, "name"));
+ artist_object = json_object_get_object_member (track_object, "artist");
+ track->track.artist = g_strdup (json_object_get_string_member (artist_object, "name"));
+ track->track.url = g_strdup (json_object_get_string_member (track_object, "url"));
+
+ g_ptr_array_add (top_tracks, track);
+ }
+ }
+
+ g_object_unref (parser);
+
+ return top_tracks;
+}
+
+/* loved tracks */
+static void
+rb_audioscrobbler_user_load_cached_loved_tracks (RBAudioscrobblerUser *user)
+{
+ char *filename;
+ char *data;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, "loved_tracks");
+
+ /* delete old data */
+ if (user->priv->loved_tracks != NULL) {
+ g_ptr_array_unref (user->priv->loved_tracks);
+ user->priv->loved_tracks = NULL;
+ }
+
+ /* load cached data if it exists */
+ if (g_file_get_contents (filename, &data, NULL, NULL) == TRUE) {
+ rb_debug ("loading cached loved tracks");
+ user->priv->loved_tracks = rb_audioscrobbler_user_parse_loved_tracks (user, data);
+ }
+
+ /* emit updated signal */
+ g_signal_emit (user, rb_audioscrobbler_user_signals[LOVED_TRACKS_UPDATED],
+ 0, user->priv->loved_tracks);
+
+ g_free (filename);
+ g_free (data);
+}
+
+static void
+rb_audioscrobbler_user_request_loved_tracks (RBAudioscrobblerUser *user)
+{
+ char *msg_url;
+ SoupMessage *msg;
+
+ rb_debug ("requesting loved tracks");
+
+ msg_url = g_strdup_printf ("%s?method=user.getLovedTracks&user=%s&api_key=%s&format=json",
+ rb_audioscrobbler_service_get_api_url (user->priv->service),
+ user->priv->username,
+ rb_audioscrobbler_service_get_api_key (user->priv->service));
+
+ msg = soup_message_new ("GET", msg_url);
+ soup_session_queue_message (user->priv->soup_session,
+ msg,
+ rb_audioscrobbler_user_loved_tracks_response_cb,
+ user);
+
+ g_free (msg_url);
+}
+
+static void
+rb_audioscrobbler_user_loved_tracks_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ RBAudioscrobblerUser *user;
+ GPtrArray *loved_tracks;
+
+ user = RB_AUDIOSCROBBLER_USER (user_data);
+ loved_tracks = rb_audioscrobbler_user_parse_loved_tracks (user, msg->response_body->data);
+
+ if (loved_tracks != NULL) {
+ rb_debug ("loved tracks request was successful");
+
+ if (user->priv->loved_tracks != NULL) {
+ g_ptr_array_unref (user->priv->loved_tracks);
+ }
+ user->priv->loved_tracks = loved_tracks;
+
+ rb_audioscrobbler_user_save_response_to_cache (user, "loved_tracks", msg->response_body->data);
+
+ g_signal_emit (user, rb_audioscrobbler_user_signals[LOVED_TRACKS_UPDATED],
+ 0, user->priv->loved_tracks);
+ } else {
+ rb_debug ("invalid response from loved tracks request");
+ }
+ } else {
+ rb_debug ("loved tracks request responded with error");
+ }
+}
+
+static GPtrArray *
+rb_audioscrobbler_user_parse_loved_tracks (RBAudioscrobblerUser *user, const char *data)
+{
+ GPtrArray *loved_tracks;
+ JsonParser *parser;
+ JsonObject *root_object;
+ JsonObject *loved_tracks_object;
+
+ loved_tracks = NULL;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, -1, NULL);
+ root_object = json_node_get_object (json_parser_get_root (parser));
+ loved_tracks_object = json_object_get_object_member (root_object, "lovedtracks");
+
+ if (json_object_has_member (loved_tracks_object, "track") == TRUE) {
+ JsonArray *track_array;
+ int i;
+
+ loved_tracks = g_ptr_array_new_with_free_func ((GDestroyNotify)rb_audioscrobbler_user_data_free);
+
+ track_array = json_object_get_array_member (loved_tracks_object, "track");
+ for (i = 0; i < json_array_get_length (track_array); i++) {
+ JsonObject *track_object;
+ JsonObject *artist_object;
+ RBAudioscrobblerUserData *track;
+
+ track_object = json_array_get_object_element (track_array, i);
+
+ track = g_slice_new0 (RBAudioscrobblerUserData);
+ track->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK;
+ track->track.title = g_strdup (json_object_get_string_member (track_object, "name"));
+ artist_object = json_object_get_object_member (track_object, "artist");
+ track->track.artist = g_strdup (json_object_get_string_member (artist_object, "name"));
+ track->track.url = g_strdup (json_object_get_string_member (track_object, "url"));
+
+ g_ptr_array_add (loved_tracks, track);
+ }
+ }
+
+ g_object_unref (parser);
+
+ return loved_tracks;
+}
+
+/* top artists */
+static void
+rb_audioscrobbler_user_load_cached_top_artists (RBAudioscrobblerUser *user)
+{
+ char *filename;
+ char *data;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, "top_artists");
+
+ /* delete old data */
+ if (user->priv->top_artists != NULL) {
+ g_ptr_array_unref (user->priv->top_artists);
+ user->priv->top_artists = NULL;
+ }
+
+ /* load cached data if it exists */
+ if (g_file_get_contents (filename, &data, NULL, NULL) == TRUE) {
+ rb_debug ("loading cached top artists");
+ user->priv->top_artists = rb_audioscrobbler_user_parse_top_artists (user, data);
+ }
+
+ /* emit updated signal */
+ g_signal_emit (user, rb_audioscrobbler_user_signals[TOP_ARTISTS_UPDATED],
+ 0, user->priv->top_artists);
+
+ g_free (filename);
+ g_free (data);
+}
+
+static void
+rb_audioscrobbler_user_request_top_artists (RBAudioscrobblerUser *user)
+{
+ char *msg_url;
+ SoupMessage *msg;
+
+ rb_debug ("requesting top artists");
+
+ msg_url = g_strdup_printf ("%s?method=user.getTopArtists&user=%s&api_key=%s&format=json",
+ rb_audioscrobbler_service_get_api_url (user->priv->service),
+ user->priv->username,
+ rb_audioscrobbler_service_get_api_key (user->priv->service));
+
+ msg = soup_message_new ("GET", msg_url);
+ soup_session_queue_message (user->priv->soup_session,
+ msg,
+ rb_audioscrobbler_user_top_artists_response_cb,
+ user);
+
+ g_free (msg_url);
+}
+
+static void
+rb_audioscrobbler_user_top_artists_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ RBAudioscrobblerUser *user;
+ GPtrArray *top_artists;
+
+ user = RB_AUDIOSCROBBLER_USER (user_data);
+ top_artists = rb_audioscrobbler_user_parse_top_artists (user, msg->response_body->data);
+
+ if (top_artists != NULL) {
+ rb_debug ("top artists request was successful");
+
+ if (user->priv->top_artists != NULL) {
+ g_ptr_array_unref (user->priv->top_artists);
+ }
+ user->priv->top_artists = top_artists;
+
+ rb_audioscrobbler_user_save_response_to_cache (user, "top_artists", msg->response_body->data);
+
+ g_signal_emit (user, rb_audioscrobbler_user_signals[TOP_ARTISTS_UPDATED],
+ 0, user->priv->top_artists);
+ } else {
+ rb_debug ("invalid response from top artists request");
+ }
+ } else {
+ rb_debug ("top artists request responded with error");
+ }
+}
+
+static GPtrArray *
+rb_audioscrobbler_user_parse_top_artists (RBAudioscrobblerUser *user, const char *data)
+{
+ GPtrArray *top_artists;
+ JsonParser *parser;
+ JsonObject *root_object;
+ JsonObject *top_artists_object;
+
+ top_artists = NULL;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, -1, NULL);
+ root_object = json_node_get_object (json_parser_get_root (parser));
+ top_artists_object = json_object_get_object_member (root_object, "topartists");
+
+ if (json_object_has_member (top_artists_object, "artist") == TRUE) {
+ JsonArray *artist_array;
+ int i;
+
+ top_artists = g_ptr_array_new_with_free_func ((GDestroyNotify)rb_audioscrobbler_user_data_free);
+
+ artist_array = json_object_get_array_member (top_artists_object, "artist");
+ for (i = 0; i < json_array_get_length (artist_array); i++) {
+ JsonObject *artist_object;
+ RBAudioscrobblerUserData *artist;
+
+ artist_object = json_array_get_object_element (artist_array, i);
+
+ artist = g_slice_new0 (RBAudioscrobblerUserData);
+ artist->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST;
+ artist->artist.name = g_strdup (json_object_get_string_member (artist_object, "name"));
+ artist->artist.url = g_strdup (json_object_get_string_member (artist_object, "url"));
+
+ g_ptr_array_add (top_artists, artist);
+ }
+ }
+
+ g_object_unref (parser);
+
+ return top_artists;
+}
+
+/* recommended artists */
+static void
+rb_audioscrobbler_user_load_cached_recommended_artists (RBAudioscrobblerUser *user)
+{
+ char *filename;
+ char *data;
+
+ filename = rb_audioscrobbler_user_calculate_cached_response_path (user, "recommended_artists");
+
+ /* delete old data */
+ if (user->priv->recommended_artists != NULL) {
+ g_ptr_array_unref (user->priv->recommended_artists);
+ user->priv->recommended_artists = NULL;
+ }
+
+ /* load cached data if it exists */
+ if (g_file_get_contents (filename, &data, NULL, NULL) == TRUE) {
+ rb_debug ("loading cached recommended artists");
+ user->priv->recommended_artists = rb_audioscrobbler_user_parse_recommended_artists (user, data);
+ }
+
+ /* emit updated signal */
+ g_signal_emit (user, rb_audioscrobbler_user_signals[RECOMMENDED_ARTISTS_UPDATED],
+ 0, user->priv->recommended_artists);
+
+ g_free (filename);
+ g_free (data);
+}
+
+static void
+rb_audioscrobbler_user_request_recommended_artists (RBAudioscrobblerUser *user)
+{
+ char *sig_arg;
+ char *sig;
+ char *msg_url;
+ SoupMessage *msg;
+
+ rb_debug ("requesting recommended artists");
+
+ sig_arg = g_strdup_printf ("api_key%smethoduser.getRecommendedArtistssk%s%s",
+ rb_audioscrobbler_service_get_api_key (user->priv->service),
+ user->priv->session_key,
+ rb_audioscrobbler_service_get_api_secret (user->priv->service));
+ sig = mkmd5 (sig_arg);
+
+ msg_url = g_strdup_printf ("%s?method=user.getRecommendedArtists&api_key=%s&api_sig=%s&sk=%s&format=json",
+ rb_audioscrobbler_service_get_api_url (user->priv->service),
+ rb_audioscrobbler_service_get_api_key (user->priv->service),
+ sig,
+ user->priv->session_key);
+
+ msg = soup_message_new ("GET", msg_url);
+ soup_session_queue_message (user->priv->soup_session,
+ msg,
+ rb_audioscrobbler_user_recommended_artists_response_cb,
+ user);
+
+ g_free (sig_arg);
+ g_free (sig);
+ g_free (msg_url);
+}
+
+static void
+rb_audioscrobbler_user_recommended_artists_response_cb (SoupSession *session,
+ SoupMessage *msg,
+ gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ RBAudioscrobblerUser *user;
+ GPtrArray *recommended_artists;
+
+ user = RB_AUDIOSCROBBLER_USER (user_data);
+ recommended_artists = rb_audioscrobbler_user_parse_recommended_artists (user, msg->response_body->data);
+
+ if (recommended_artists != NULL) {
+ rb_debug ("recommended artists request was successful");
+
+ if (user->priv->recommended_artists != NULL) {
+ g_ptr_array_unref (user->priv->recommended_artists);
+ }
+ user->priv->recommended_artists = recommended_artists;
+
+ rb_audioscrobbler_user_save_response_to_cache (user, "recommended_artists", msg->response_body->data);
+
+ g_signal_emit (user, rb_audioscrobbler_user_signals[RECOMMENDED_ARTISTS_UPDATED],
+ 0, user->priv->recommended_artists);
+ } else {
+ rb_debug ("invalid response from recommended artists request");
+ }
+ } else {
+ rb_debug ("recommended artists request responded with error");
+ }
+}
+
+static GPtrArray *
+rb_audioscrobbler_user_parse_recommended_artists (RBAudioscrobblerUser *user, const char *data)
+{
+ GPtrArray *recommended_artists;
+ JsonParser *parser;
+ JsonObject *root_object;
+
+ recommended_artists = NULL;
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, -1, NULL);
+ root_object = json_node_get_object (json_parser_get_root (parser));
+
+ if (json_object_has_member (root_object, "error")) {
+ /* probably bad authentication. Unlike with scrobbling or radio playback,
+ * this is not a problem: we'll just live with no recommendations */
+ rb_debug ("user.getRecommendedArtists failed due to bad authentication");
+ } else {
+ JsonObject *recommended_artists_object;
+ recommended_artists_object = json_object_get_object_member (root_object, "recommendations");
+
+ if (json_object_has_member (recommended_artists_object, "artist") == TRUE) {
+ JsonArray *artist_array;
+ int i;
+
+ recommended_artists = g_ptr_array_new_with_free_func ((GDestroyNotify)rb_audioscrobbler_user_data_free);
+
+ artist_array = json_object_get_array_member (recommended_artists_object, "artist");
+ for (i = 0; i < json_array_get_length (artist_array); i++) {
+ JsonObject *artist_object;
+ RBAudioscrobblerUserData *artist;
+
+ artist_object = json_array_get_object_element (artist_array, i);
+
+ artist = g_slice_new0 (RBAudioscrobblerUserData);
+ artist->type = RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST;
+ artist->artist.name = g_strdup (json_object_get_string_member (artist_object, "name"));
+ artist->artist.url = g_strdup (json_object_get_string_member (artist_object, "url"));
+
+ g_ptr_array_add (recommended_artists, artist);
+ }
+ }
+ }
+
+ g_object_unref (parser);
+
+ return recommended_artists;
+}
diff --git a/plugins/audioscrobbler/rb-audioscrobbler-user.h b/plugins/audioscrobbler/rb-audioscrobbler-user.h
new file mode 100644
index 0000000..6c460fb
--- /dev/null
+++ b/plugins/audioscrobbler/rb-audioscrobbler-user.h
@@ -0,0 +1,104 @@
+/*
+ * rb-audioscrobbler-profile-user.h
+ *
+ * Copyright (C) 2010 Jamie Nicol <jamie thenicols net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The Rhythmbox authors hereby grant permission for non-GPL compatible
+ * GStreamer plugins to be used and distributed together with GStreamer
+ * and Rhythmbox. This permission is above and beyond the permissions granted
+ * by the GPL license by which Rhythmbox is covered. If you modify this code
+ * you may extend this exception to your version of the code, but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __RB_AUDIOSCROBBLER_USER_H
+#define __RB_AUDIOSCROBBLER_USER_H
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "rb-audioscrobbler-service.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_AUDIOSCROBBLER_USER_DATA (rb_audioscrobbler_user_data_get_type ())
+#define RB_AUDIOSCROBBLER_USER_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_AUDIOSCROBBLER_USER_DATA, RBAudioscrobblerUserData))
+#define RB_IS_AUDIOSCROBBLER_USER_DATA(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_AUDIOSCROBBLER_USER_DATA))
+typedef struct {
+ enum {
+ RB_AUDIOSCROBBLER_USER_DATA_TYPE_USER_INFO,
+ RB_AUDIOSCROBBLER_USER_DATA_TYPE_TRACK,
+ RB_AUDIOSCROBBLER_USER_DATA_TYPE_ARTIST
+ } type;
+
+ union {
+ struct {
+ char *username;
+ char *real_name;
+ char *url;
+ GdkPixbuf *image;
+ } user_info;
+
+ struct {
+ char *title;
+ char *artist;
+ char *url;
+ GdkPixbuf *image;
+ } track;
+
+ struct {
+ char *name;
+ char *url;
+ GdkPixbuf *image;
+ } artist;
+ };
+} RBAudioscrobblerUserData;
+GType rb_audioscrobbler_user_data_get_type (void);
+
+#define RB_TYPE_AUDIOSCROBBLER_USER (rb_audioscrobbler_user_get_type ())
+#define RB_AUDIOSCROBBLER_USER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_AUDIOSCROBBLER_USER, RBAudioscrobblerUser))
+#define RB_AUDIOSCROBBLER_USER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_AUDIOSCROBBLER_USER, RBAudioscrobblerUserClass))
+#define RB_IS_AUDIOSCROBBLER_USER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_AUDIOSCROBBLER_USER))
+#define RB_IS_AUDIOSCROBBLER_USER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_AUDIOSCROBBLER_USER))
+#define RB_AUDIOSCROBBLER_USER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_AUDIOSCROBBLER_USER, RBAudioscrobblerUserClass))
+
+typedef struct _RBAudioscrobblerUserPrivate RBAudioscrobblerUserPrivate;
+
+typedef struct
+{
+ GObject parent;
+
+ RBAudioscrobblerUserPrivate *priv;
+} RBAudioscrobblerUser;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} RBAudioscrobblerUserClass;
+
+GType rb_audioscrobbler_user_get_type (void);
+RBAudioscrobblerUser *rb_audioscrobbler_user_new (RBAudioscrobblerService *service);
+
+void rb_audioscrobbler_user_set_authentication_details (RBAudioscrobblerUser *user,
+ const char *username,
+ const char *session_key);
+
+void rb_audioscrobbler_user_update (RBAudioscrobblerUser *user);
+
+G_END_DECLS
+
+#endif /* __RB_AUDIOSCROBBLER_USER_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]