No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2005 Alex Revo <xiphoidappendix@gmail.com>,
4 * Ruben Vermeersch <ruben@Lambda1.be>
5 * (C) 2007 Christophe Fergeau <teuf@gnome.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * The Rhythmbox authors hereby grant permission for non-GPL compatible
13 * GStreamer plugins to be used and distributed together with GStreamer
14 * and Rhythmbox. This permission is above and beyond the permissions granted
15 * by the GPL license by which Rhythmbox is covered. If you modify this code
16 * you may extend this exception to your version of the code, but you are not
17 * obligated to do so. If you do not wish to do so, delete this exception
18 * statement from your version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 *
29 */
30
31 #define __EXTENSIONS__
32
33 #include "config.h"
34
35 #include <errno.h>
36
37 #include <string.h>
38 #include <time.h>
39
40 #include <glib.h>
41 #include <glib/gi18n.h>
42 #include <glib/gprintf.h>
43 #include <gtk/gtk.h>
44
45 #include <libsoup/soup.h>
46 #include <libsoup/soup-gnome.h>
47
48 #include "rb-audioscrobbler.h"
49 #include "rb-debug.h"
50 #include "rb-file-helpers.h"
51 #include "rb-builder-helpers.h"
52 #include "rb-shell.h"
53 #include "rb-shell-player.h"
54 #include "rb-source.h"
55 #include "rb-cut-and-paste-code.h"
56 #include "rb-util.h"
57 #include "rb-podcast-entry-types.h"
58 #include "rb-marshal.h"
59 #include "rb-audioscrobbler-entry.h"
60
61 #define CLIENT_ID "rbx"
62 #define CLIENT_VERSION VERSION
63
64 #define MAX_QUEUE_SIZE 1000
65 #define MAX_SUBMIT_SIZE 50
66 #define INITIAL_HANDSHAKE_DELAY 60
67 #define MAX_HANDSHAKE_DELAY 120*60
68
69 #define SCROBBLER_VERSION "1.2.1"
70
71 #define USER_AGENT "Rhythmbox/" VERSION
72
73 struct _RBAudioscrobblerPrivate
74 {
75 RBAudioscrobblerService *service;
76
77 RBShellPlayer *shell_player;
78
79 /* Data for the prefs pane */
80 guint submit_count;
81 char *submit_time;
82 guint queue_count;
83 enum {
84 STATUS_OK = 0,
85 HANDSHAKING,
86 REQUEST_FAILED,
87 BADAUTH,
88 BAD_TIMESTAMP,
89 CLIENT_BANNED,
90 GIVEN_UP
91 } status;
92 char *status_msg;
93
94 /* Submission queue */
95 GQueue *queue;
96 /* Entries currently being submitted */
97 GQueue *submission;
98
99 guint failures;
100 guint handshake_delay;
101 /* Handshake has been done? */
102 gboolean handshake;
103 time_t handshake_next;
104
105 /* Only write the queue to a file if it has been changed */
106 gboolean queue_changed;
107
108 /* Authentication cookie + authentication info */
109 gchar *sessionid;
110 gchar *username;
111 gchar *session_key;
112 gchar *submit_url;
113 gchar *nowplaying_url;
114
115 /* Currently playing song info, if NULL this means the currently
116 * playing song isn't eligible to be queued
117 */
118 AudioscrobblerEntry *currently_playing;
119 guint current_elapsed;
120 gboolean now_playing_updated;
121
122 guint timeout_id;
123
124 /* HTTP requests session */
125 SoupSession *soup_session;
126
127 /* callback for songs that were played offline (eg on an iPod) */
128 gulong offline_play_notify_id;
129 };
130
131 #define RB_AUDIOSCROBBLER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_AUDIOSCROBBLER, RBAudioscrobblerPrivate))
132
133
134 static gboolean rb_audioscrobbler_load_queue (RBAudioscrobbler *audioscrobbler);
135 static int rb_audioscrobbler_save_queue (RBAudioscrobbler *audioscrobbler);
136 static void rb_audioscrobbler_print_queue (RBAudioscrobbler *audioscrobbler, gboolean submission);
137 static void rb_audioscrobbler_free_queue_entries (RBAudioscrobbler *audioscrobbler, GQueue **queue);
138
139 static void rb_audioscrobbler_get_property (GObject *object,
140 guint prop_id,
141 GValue *value,
142 GParamSpec *pspec);
143 static void rb_audioscrobbler_set_property (GObject *object,
144 guint prop_id,
145 const GValue *value,
146 GParamSpec *pspec);
147 static void rb_audioscrobbler_dispose (GObject *object);
148 static void rb_audioscrobbler_finalize (GObject *object);
149
150 static void rb_audioscrobbler_add_timeout (RBAudioscrobbler *audioscrobbler);
151 static gboolean rb_audioscrobbler_timeout_cb (RBAudioscrobbler *audioscrobbler);
152
153 static void rb_audioscrobbler_parse_response (RBAudioscrobbler *audioscrobbler, SoupMessage *msg, gboolean handshake);
154
155 static void rb_audioscrobbler_do_handshake (RBAudioscrobbler *audioscrobbler);
156 static void rb_audioscrobbler_submit_queue (RBAudioscrobbler *audioscrobbler);
157 static void rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler,
158 char *url,
159 char *post_data,
160 SoupSessionCallback response_handler);
161 static void rb_audioscrobbler_do_handshake_cb (SoupSession *session, SoupMessage *msg, gpointer user_data);
162 static void rb_audioscrobbler_submit_queue_cb (SoupSession *session, SoupMessage *msg, gpointer user_data);
163 static void rb_audioscrobbler_nowplaying_cb (SoupSession *session, SoupMessage *msg, gpointer user_data);
164
165 static void rb_audioscrobbler_song_changed_cb (RBShellPlayer *player,
166 RhythmDBEntry *entry,
167 RBAudioscrobbler *audioscrobbler);
168 static void rb_audioscrobbler_offline_play_notify_cb (RhythmDB *db,
169 RhythmDBEntry *rb_entry,
170 const gchar *property_name,
171 const GValue *metadata,
172 RBAudioscrobbler *audioscrobbler);
173
174 static void rb_audioscrobbler_nowplaying (RBAudioscrobbler *audioscrobbler, AudioscrobblerEntry *entry);
175
176
177
178
179 enum
180 {
181 PROP_0,
182 PROP_SERVICE,
183 PROP_SHELL_PLAYER,
184 PROP_USERNAME,
185 PROP_SESSION_KEY,
186 };
187
188 enum
189 {
190 AUTHENTICATION_ERROR,
191 STATISTICS_CHANGED,
192 LAST_SIGNAL
193 };
194
195 static guint rb_audioscrobbler_signals[LAST_SIGNAL] = { 0 };
196
197 G_DEFINE_DYNAMIC_TYPE (RBAudioscrobbler, rb_audioscrobbler, G_TYPE_OBJECT)
198
199
200 static void
201 rb_audioscrobbler_constructed (GObject *object)
202 {
203 RBAudioscrobbler *audioscrobbler;
204 RhythmDB *db;
205 RhythmDBEntry *playing_entry;
206
207 RB_CHAIN_GOBJECT_METHOD (rb_audioscrobbler_parent_class, constructed, object);
208
209 audioscrobbler = RB_AUDIOSCROBBLER (object);
210
211 rb_audioscrobbler_load_queue (audioscrobbler);
212 rb_audioscrobbler_add_timeout (audioscrobbler);
213 rb_audioscrobbler_statistics_changed (audioscrobbler);
214
215 g_object_get (audioscrobbler->priv->shell_player, "db", &db, NULL);
216
217 audioscrobbler->priv->offline_play_notify_id =
218 g_signal_connect_object (db,
219 "entry-extra-metadata-notify::rb:offlinePlay",
220 (GCallback)rb_audioscrobbler_offline_play_notify_cb,
221 audioscrobbler, 0);
222
223 /* if an entry is currently being played then handle it */
224 playing_entry = rb_shell_player_get_playing_entry (audioscrobbler->priv->shell_player);
225 if (playing_entry != NULL) {
226 rb_audioscrobbler_song_changed_cb (audioscrobbler->priv->shell_player,
227 playing_entry,
228 audioscrobbler);
229 rhythmdb_entry_unref (playing_entry);
230 }
231
232 g_object_unref (db);
233 }
234
235 /* Class-related functions: */
236 static void
237 rb_audioscrobbler_class_init (RBAudioscrobblerClass *klass)
238 {
239 GObjectClass *object_class = G_OBJECT_CLASS (klass);
240
241 object_class->constructed = rb_audioscrobbler_constructed;
242 object_class->dispose = rb_audioscrobbler_dispose;
243 object_class->finalize = rb_audioscrobbler_finalize;
244
245 object_class->set_property = rb_audioscrobbler_set_property;
246 object_class->get_property = rb_audioscrobbler_get_property;
247
248 g_object_class_install_property (object_class,
249 PROP_SERVICE,
250 g_param_spec_object ("service",
251 "Service",
252 "Audioscrobbler service to scrobble to",
253 RB_TYPE_AUDIOSCROBBLER_SERVICE,
254 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
255
256 g_object_class_install_property (object_class,
257 PROP_SHELL_PLAYER,
258 g_param_spec_object ("shell-player",
259 "RBShellPlayer",
260 "RBShellPlayer object",
261 RB_TYPE_SHELL_PLAYER,
262 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
263
264 g_object_class_install_property (object_class,
265 PROP_USERNAME,
266 g_param_spec_string ("username",
267 "Username",
268 "Username of the user who is scrobbling data",
269 NULL,
270 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
271
272 g_object_class_install_property (object_class,
273 PROP_SESSION_KEY,
274 g_param_spec_string ("session-key",
275 "Session Key",
276 "Session key used to authenticate the user",
277 NULL,
278 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
279
280 /**
281 * RBAudioscrobbler::authentication-error:
282 * @account: the #RBAudioscrobblerAccount
283 * @status: new status
284 *
285 * Emitted when an authentication error occurs.
286 */
287 rb_audioscrobbler_signals[AUTHENTICATION_ERROR] =
288 g_signal_new ("authentication-error",
289 G_OBJECT_CLASS_TYPE (object_class),
290 G_SIGNAL_RUN_LAST,
291 G_STRUCT_OFFSET (RBAudioscrobblerClass, authentication_error),
292 NULL, NULL,
293 g_cclosure_marshal_VOID__VOID,
294 G_TYPE_NONE,
295 0);
296
297 /**
298 * RBAudioscrobbler::statistics-changed:
299 * @status_msg: description of the status
300 * @queued_count: the number of tracks queued for submission
301 * @submit_count: the number of tracks already submitted this session
302 * @submit_time: the time at which the last submission was made
303 *
304 * Emitted when the scrobbling session's statistics change.
305 */
306 rb_audioscrobbler_signals[STATISTICS_CHANGED] =
307 g_signal_new ("statistics-changed",
308 G_OBJECT_CLASS_TYPE (object_class),
309 G_SIGNAL_RUN_LAST,
310 G_STRUCT_OFFSET (RBAudioscrobblerClass, statistics_changed),
311 NULL, NULL,
312 rb_marshal_VOID__STRING_UINT_UINT_STRING,
313 G_TYPE_NONE,
314 4,
315 G_TYPE_STRING,
316 G_TYPE_UINT,
317 G_TYPE_UINT,
318 G_TYPE_STRING);
319
320 g_type_class_add_private (klass, sizeof (RBAudioscrobblerPrivate));
321 }
322
323 static void
324 rb_audioscrobbler_class_finalize (RBAudioscrobblerClass *klass)
325 {
326 }
327
328 static void
329 rb_audioscrobbler_init (RBAudioscrobbler *audioscrobbler)
330 {
331 rb_debug ("Initialising Audioscrobbler");
332 rb_debug ("Plugin ID: %s, Version %s (Protocol %s)",
333 CLIENT_ID, CLIENT_VERSION, SCROBBLER_VERSION);
334
335 audioscrobbler->priv = RB_AUDIOSCROBBLER_GET_PRIVATE (audioscrobbler);
336
337 audioscrobbler->priv->queue = g_queue_new();
338 audioscrobbler->priv->submission = g_queue_new();
339 audioscrobbler->priv->sessionid = g_strdup ("");
340 audioscrobbler->priv->username = NULL;
341 audioscrobbler->priv->session_key = NULL;
342 audioscrobbler->priv->submit_url = g_strdup ("");
343 audioscrobbler->priv->nowplaying_url = g_strdup ("");
344 }
345
346 static void
347 rb_audioscrobbler_dispose (GObject *object)
348 {
349 RBAudioscrobbler *audioscrobbler;
350
351 g_return_if_fail (object != NULL);
352 g_return_if_fail (RB_IS_AUDIOSCROBBLER (object));
353
354 audioscrobbler = RB_AUDIOSCROBBLER (object);
355
356 rb_debug ("disposing audioscrobbler");
357
358 /* Save any remaining entries */
359 rb_audioscrobbler_save_queue (audioscrobbler);
360
361 if (audioscrobbler->priv->offline_play_notify_id != 0) {
362 RhythmDB *db;
363
364 g_object_get (G_OBJECT (audioscrobbler->priv->shell_player),
365 "db", &db,
366 NULL);
367 g_signal_handler_disconnect (db, audioscrobbler->priv->offline_play_notify_id);
368 audioscrobbler->priv->offline_play_notify_id = 0;
369 g_object_unref (db);
370 }
371
372 if (audioscrobbler->priv->timeout_id != 0) {
373 g_source_remove (audioscrobbler->priv->timeout_id);
374 audioscrobbler->priv->timeout_id = 0;
375 }
376
377 if (audioscrobbler->priv->soup_session != NULL) {
378 soup_session_abort (audioscrobbler->priv->soup_session);
379 g_object_unref (audioscrobbler->priv->soup_session);
380 audioscrobbler->priv->soup_session = NULL;
381 }
382
383 if (audioscrobbler->priv->service != NULL) {
384 g_object_unref (audioscrobbler->priv->service);
385 audioscrobbler->priv->service = NULL;
386 }
387
388 if (audioscrobbler->priv->shell_player != NULL) {
389 g_object_unref (audioscrobbler->priv->shell_player);
390 audioscrobbler->priv->shell_player = NULL;
391 }
392
393 G_OBJECT_CLASS (rb_audioscrobbler_parent_class)->dispose (object);
394 }
395
396 static void
397 rb_audioscrobbler_finalize (GObject *object)
398 {
399 RBAudioscrobbler *audioscrobbler;
400
401 rb_debug ("Finalizing Audioscrobbler");
402
403 g_return_if_fail (object != NULL);
404 g_return_if_fail (RB_IS_AUDIOSCROBBLER (object));
405
406 audioscrobbler = RB_AUDIOSCROBBLER (object);
407
408 g_free (audioscrobbler->priv->sessionid);
409 g_free (audioscrobbler->priv->username);
410 g_free (audioscrobbler->priv->session_key);
411 g_free (audioscrobbler->priv->submit_url);
412 g_free (audioscrobbler->priv->nowplaying_url);
413
414 if (audioscrobbler->priv->currently_playing != NULL) {
415 rb_audioscrobbler_entry_free (audioscrobbler->priv->currently_playing);
416 audioscrobbler->priv->currently_playing = NULL;
417 }
418
419 rb_audioscrobbler_free_queue_entries (audioscrobbler, &audioscrobbler->priv->queue);
420 rb_audioscrobbler_free_queue_entries (audioscrobbler, &audioscrobbler->priv->submission);
421
422 G_OBJECT_CLASS (rb_audioscrobbler_parent_class)->finalize (object);
423 }
424
425 RBAudioscrobbler*
426 rb_audioscrobbler_new (RBAudioscrobblerService *service,
427 RBShellPlayer *shell_player,
428 const char *username,
429 const char *session_key)
430 {
431 return g_object_new (RB_TYPE_AUDIOSCROBBLER,
432 "service", service,
433 "shell-player", shell_player,
434 "username", username,
435 "session_key", session_key,
436 NULL);
437 }
438
439 static void
440 rb_audioscrobbler_set_property (GObject *object,
441 guint prop_id,
442 const GValue *value,
443 GParamSpec *pspec)
444 {
445 RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (object);
446
447 switch (prop_id) {
448 case PROP_SERVICE:
449 audioscrobbler->priv->service = g_value_dup_object (value);
450 break;
451 case PROP_SHELL_PLAYER:
452 audioscrobbler->priv->shell_player = g_value_get_object (value);
453 g_object_ref (G_OBJECT (audioscrobbler->priv->shell_player));
454 g_signal_connect_object (G_OBJECT (audioscrobbler->priv->shell_player),
455 "playing-song-changed",
456 G_CALLBACK (rb_audioscrobbler_song_changed_cb),
457 audioscrobbler, 0);
458 break;
459 case PROP_USERNAME:
460 audioscrobbler->priv->username = g_value_dup_string (value);
461 break;
462 case PROP_SESSION_KEY:
463 audioscrobbler->priv->session_key = g_value_dup_string (value);
464 break;
465 default:
466 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
467 break;
468 }
469 }
470
471 static void
472 rb_audioscrobbler_get_property (GObject *object,
473 guint prop_id,
474 GValue *value,
475 GParamSpec *pspec)
476 {
477 RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (object);
478
479 switch (prop_id) {
480 case PROP_SHELL_PLAYER:
481 g_value_set_object (value, audioscrobbler->priv->shell_player);
482 break;
483 default:
484 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
485 break;
486 }
487 }
488
489 /* emits the statistics-changed signal */
490 void
491 rb_audioscrobbler_statistics_changed (RBAudioscrobbler *audioscrobbler)
492 {
493 const char *status;
494 char *status_msg;
495
496 switch (audioscrobbler->priv->status) {
497 case STATUS_OK:
498 status = _("OK");
499 break;
500 case HANDSHAKING:
501 status = _("Logging in");
502 break;
503 case REQUEST_FAILED:
504 status = _("Request failed");
505 break;
506 case BADAUTH:
507 status = _("Authentication error");
508 break;
509 case BAD_TIMESTAMP:
510 status = _("Clock is not set correctly");
511 break;
512 case CLIENT_BANNED:
513 status = _("This version of Rhythmbox has been banned.");
514 break;
515 case GIVEN_UP:
516 status = _("Track submission failed too many times");
517 break;
518 default:
519 g_assert_not_reached ();
520 break;
521 }
522
523 if (audioscrobbler->priv->status_msg && audioscrobbler->priv->status_msg[0] != '\0') {
524 status_msg = g_strdup_printf ("%s: %s", status, audioscrobbler->priv->status_msg);
525 } else {
526 status_msg = g_strdup (status);
527 }
528
529 g_signal_emit_by_name (audioscrobbler, "statistics-changed",
530 status_msg, audioscrobbler->priv->queue_count,
531 audioscrobbler->priv->submit_count, audioscrobbler->priv->submit_time);
532
533 g_free (status_msg);
534 }
535
536 /* Add the audioscrobbler thread timer */
537 static void
538 rb_audioscrobbler_add_timeout (RBAudioscrobbler *audioscrobbler)
539 {
540 if (!audioscrobbler->priv->timeout_id) {
541 rb_debug ("Adding Audioscrobbler timer (15 seconds)");
542 audioscrobbler->priv->timeout_id =
543 g_timeout_add_seconds (15,
544 (GSourceFunc) rb_audioscrobbler_timeout_cb,
545 audioscrobbler);
546 }
547 }
548
549 static gboolean
550 rb_audioscrobbler_is_queueable (RhythmDBEntry *entry)
551 {
552 const char *title;
553 const char *artist;
554 gulong duration;
555 RhythmDBEntryType *type;
556 RhythmDBEntryCategory category;
557
558 /* First, check if the entry is appropriate for sending to
559 * audioscrobbler
560 */
561 type = rhythmdb_entry_get_entry_type (entry);
562 g_object_get (type, "category", &category, NULL);
563 if (category != RHYTHMDB_ENTRY_NORMAL) {
564 rb_debug ("entry %s is not queueable: category not NORMAL", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
565 return FALSE;
566 }
567 if (type == RHYTHMDB_ENTRY_TYPE_PODCAST_POST) {
568 rb_debug ("entry %s is not queueable: is a podcast post", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
569 return FALSE;
570 }
571 if (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR)) {
572 rb_debug ("entry %s is not queueable: has playback error (%s)",
573 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION),
574 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_PLAYBACK_ERROR));
575 return FALSE;
576 }
577
578 title = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
579 artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
580 duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
581
582 /* The specification (v1.1) tells us to ignore entries that do not
583 * meet these conditions
584 */
585 if (duration < 30) {
586 rb_debug ("entry %s not queueable: shorter than 30 seconds", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
587 return FALSE;
588 }
589 if (strcmp (artist, _("Unknown")) == 0) {
590 rb_debug ("entry %s not queueable: artist is %s (unknown)", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), artist);
591 return FALSE;
592 }
593 if (strcmp (title, _("Unknown")) == 0) {
594 rb_debug ("entry %s not queueable: title is %s (unknown)", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), title);
595 return FALSE;
596 }
597
598 rb_debug ("entry %s is queueable", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
599 return TRUE;
600 }
601
602
603 static void
604 rb_audioscrobbler_add_to_queue (RBAudioscrobbler *audioscrobbler,
605 AudioscrobblerEntry *entry)
606 {
607 if (g_queue_get_length (audioscrobbler->priv->queue) >= MAX_QUEUE_SIZE) {
608 AudioscrobblerEntry *oldest;
609
610 rb_debug ("queue limit reached. dropping oldest entry.");
611 oldest = g_queue_pop_head (audioscrobbler->priv->queue);
612 rb_audioscrobbler_entry_free (oldest);
613 } else {
614 audioscrobbler->priv->queue_count++;
615 }
616
617 g_queue_push_tail (audioscrobbler->priv->queue, entry);
618 audioscrobbler->priv->queue_changed = TRUE;
619 }
620
621 static void
622 maybe_add_current_song_to_queue (RBAudioscrobbler *audioscrobbler)
623 {
624 gboolean got_elapsed;
625 guint elapsed;
626 AudioscrobblerEntry *cur_entry;
627
628 cur_entry = audioscrobbler->priv->currently_playing;
629 if (cur_entry == NULL) {
630 return;
631 }
632
633 got_elapsed = rb_shell_player_get_playing_time (audioscrobbler->priv->shell_player,
634 &elapsed,
635 NULL);
636 if (got_elapsed) {
637 int elapsed_delta = elapsed - audioscrobbler->priv->current_elapsed;
638 audioscrobbler->priv->current_elapsed = elapsed;
639
640 if ((elapsed >= cur_entry->length / 2 || elapsed >= 240) && elapsed_delta < 20) {
641 rb_debug ("Adding currently playing song to queue");
642 rb_audioscrobbler_add_to_queue (audioscrobbler, cur_entry);
643 audioscrobbler->priv->currently_playing = NULL;
644
645 rb_audioscrobbler_statistics_changed (audioscrobbler);
646 } else if (elapsed_delta > 20) {
647 rb_debug ("Skipping detected; not submitting current song");
648 /* not sure about this - what if I skip to somewhere towards
649 * the end, but then go back and listen to the whole song?
650 */
651 rb_audioscrobbler_entry_free (audioscrobbler->priv->currently_playing);
652 audioscrobbler->priv->currently_playing = NULL;
653 }
654 }
655 }
656
657 /* updates the queue and submits entries as required */
658 static gboolean
659 rb_audioscrobbler_timeout_cb (RBAudioscrobbler *audioscrobbler)
660 {
661 maybe_add_current_song_to_queue (audioscrobbler);
662
663 /* do handshake if we need to */
664 rb_audioscrobbler_do_handshake (audioscrobbler);
665
666 if ((audioscrobbler->priv->now_playing_updated == FALSE) &&
667 (audioscrobbler->priv->currently_playing != NULL) &&
668 audioscrobbler->priv->handshake) {
669 rb_debug ("Sending now playing data");
670 audioscrobbler->priv->now_playing_updated = TRUE;
671 rb_audioscrobbler_nowplaying (audioscrobbler, audioscrobbler->priv->currently_playing);
672 }
673
674 /* if there's something in the queue, submit it if we can, save it otherwise */
675 if (!g_queue_is_empty(audioscrobbler->priv->queue)) {
676 if (audioscrobbler->priv->handshake) {
677 rb_audioscrobbler_submit_queue (audioscrobbler);
678 } else {
679 rb_audioscrobbler_save_queue (audioscrobbler);
680 }
681 }
682 return TRUE;
683 }
684
685 static void
686 rb_audioscrobbler_offline_play_notify_cb (RhythmDB *db,
687 RhythmDBEntry *rb_entry,
688 const gchar *property_name,
689 const GValue *metadata,
690 RBAudioscrobbler *audioscrobbler)
691 {
692 g_return_if_fail (G_VALUE_HOLDS_ULONG (metadata));
693
694 /* FIXME: do sanity checks on play_date value? */
695 if (rb_audioscrobbler_is_queueable (rb_entry)) {
696 AudioscrobblerEntry *as_entry;
697
698 as_entry = rb_audioscrobbler_entry_create (rb_entry, audioscrobbler->priv->service);
699 as_entry->play_time = g_value_get_ulong (metadata);
700 rb_audioscrobbler_add_to_queue (audioscrobbler, as_entry);
701 }
702 }
703
704 static void
705 rb_audioscrobbler_parse_response (RBAudioscrobbler *audioscrobbler, SoupMessage *msg, gboolean handshake)
706 {
707 gboolean successful;
708
709 rb_debug ("Parsing response, status=%d Reason: %s", msg->status_code, msg->reason_phrase);
710
711 successful = FALSE;
712 if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) && msg->response_body->length != 0)
713 successful = TRUE;
714
715 if (successful) {
716 gchar **breaks;
717
718 breaks = g_strsplit (msg->response_body->data, "\n", 0);
719
720 g_free (audioscrobbler->priv->status_msg);
721 audioscrobbler->priv->status = STATUS_OK;
722 audioscrobbler->priv->status_msg = NULL;
723
724 if (g_str_has_prefix (breaks[0], "OK")) {
725 rb_debug ("OK");
726 if (handshake) {
727 if (g_strv_length (breaks) < 4) {
728 g_warning ("Unexpectedly short successful last.fm handshake response:\n%s",
729 msg->response_body->data);
730 audioscrobbler->priv->status = REQUEST_FAILED;
731 } else {
732 g_free (audioscrobbler->priv->sessionid);
733 g_free (audioscrobbler->priv->nowplaying_url);
734 g_free (audioscrobbler->priv->submit_url);
735 audioscrobbler->priv->sessionid = g_strdup (breaks[1]);
736 audioscrobbler->priv->nowplaying_url = g_strdup (breaks[2]);
737 audioscrobbler->priv->submit_url = g_strdup (breaks[3]);
738 }
739 }
740 } else if (g_str_has_prefix (breaks[0], "BANNED")) {
741 rb_debug ("Client banned");
742 audioscrobbler->priv->status = CLIENT_BANNED;
743 } else if (g_str_has_prefix (breaks[0], "BADAUTH")) {
744 rb_debug ("Bad authorization");
745 audioscrobbler->priv->status = BADAUTH;
746 /* emit an authentication error signal.
747 * this is the only error which needs to be addressed from outside this class */
748 g_signal_emit (audioscrobbler, rb_audioscrobbler_signals[AUTHENTICATION_ERROR], 0);
749 } else if (g_str_has_prefix (breaks[0], "BADTIME")) {
750 rb_debug ("Bad timestamp");
751 audioscrobbler->priv->status = BAD_TIMESTAMP;
752 } else if (g_str_has_prefix (breaks[0], "FAILED")) {
753 rb_debug ("Server failure:\n \tMessage: %s", breaks[0]);
754 audioscrobbler->priv->status = REQUEST_FAILED;
755 /* this is probably going to be ugly, but there isn't much we can do */
756 if (strlen (breaks[0]) > strlen ("FAILED ")) {
757 audioscrobbler->priv->status_msg = g_strdup (breaks[0] + strlen ("FAILED "));
758 }
759 } else {
760 g_warning ("Unexpected last.fm response:\n%s",
761 msg->response_body->data);
762 audioscrobbler->priv->status = REQUEST_FAILED;
763 }
764
765 g_strfreev (breaks);
766 } else {
767 audioscrobbler->priv->status = REQUEST_FAILED;
768 audioscrobbler->priv->status_msg = g_strdup (msg->reason_phrase);
769 }
770 }
771
772 static gboolean
773 idle_unref_cb (GObject *object)
774 {
775 g_object_unref (object);
776 return FALSE;
777 }
778
779 /*
780 * NOTE: the caller *must* unref the audioscrobbler object in an idle
781 * handler created in the callback.
782 */
783 static void
784 rb_audioscrobbler_perform (RBAudioscrobbler *audioscrobbler,
785 char *url,
786 char *post_data,
787 SoupSessionCallback response_handler)
788 {
789 SoupMessage *msg;
790
791 msg = soup_message_new (post_data == NULL ? "GET" : "POST", url);
792 soup_message_headers_append (msg->request_headers, "User-Agent", USER_AGENT);
793
794 if (post_data != NULL) {
795 rb_debug ("Submitting to Audioscrobbler: %s", post_data);
796 soup_message_set_request (msg,
797 "application/x-www-form-urlencoded",
798 SOUP_MEMORY_TAKE,
799 post_data,
800 strlen (post_data));
801 }
802
803 /* create soup session, if we haven't got one yet */
804 if (!audioscrobbler->priv->soup_session) {
805 audioscrobbler->priv->soup_session =
806 soup_session_async_new_with_options (
807 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
808 NULL);
809 }
810
811 soup_session_queue_message (audioscrobbler->priv->soup_session,
812 msg,
813 response_handler,
814 g_object_ref (audioscrobbler));
815 }
816
817 static gboolean
818 rb_audioscrobbler_should_handshake (RBAudioscrobbler *audioscrobbler)
819 {
820 /* Perform handshake if necessary. Only perform handshake if
821 * - we have no current handshake; AND
822 * - we have waited the appropriate amount of time between
823 * handshakes; AND
824 * - we have a username; AND
825 * - we have a session key
826 */
827 if (audioscrobbler->priv->handshake) {
828 return FALSE;
829 }
830
831 if (time (NULL) < audioscrobbler->priv->handshake_next) {
832 rb_debug ("Too soon; time=%ld, handshake_next=%ld",
833 (long)time (NULL),
834 (long)audioscrobbler->priv->handshake_next);
835 return FALSE;
836 }
837
838 if ((audioscrobbler->priv->username == NULL) ||
839 (strcmp (audioscrobbler->priv->username, "") == 0)) {
840 rb_debug ("No username set");
841 return FALSE;
842 }
843
844 if ((audioscrobbler->priv->session_key == NULL) ||
845 (strcmp (audioscrobbler->priv->session_key, "") == 0)) {
846 return FALSE;
847 }
848
849 return TRUE;
850 }
851
852 static void
853 rb_audioscrobbler_do_handshake (RBAudioscrobbler *audioscrobbler)
854 {
855 gchar *username;
856 gchar *url;
857 gchar *auth;
858 gchar *autharg;
859 guint timestamp;
860
861 if (!rb_audioscrobbler_should_handshake (audioscrobbler)) {
862 return;
863 }
864
865 username = soup_uri_encode (audioscrobbler->priv->username, EXTRA_URI_ENCODE_CHARS);
866 timestamp = time (NULL);
867
868 autharg = g_strdup_printf ("%s%d",
869 rb_audioscrobbler_service_get_api_secret (audioscrobbler->priv->service),
870 timestamp);
871 auth = g_compute_checksum_for_string (G_CHECKSUM_MD5, autharg, -1);
872
873 url = g_strdup_printf ("%s?hs=true&p=%s&c=%s&v=%s&u=%s&t=%d&a=%s&api_key=%s&sk=%s",
874 rb_audioscrobbler_service_get_scrobbler_url (audioscrobbler->priv->service),
875 SCROBBLER_VERSION,
876 CLIENT_ID,
877 CLIENT_VERSION,
878 username,
879 timestamp,
880 auth,
881 rb_audioscrobbler_service_get_api_key (audioscrobbler->priv->service),
882 audioscrobbler->priv->session_key);
883
884 g_free (auth);
885 g_free (autharg);
886 g_free (username);
887
888 rb_debug ("Performing handshake with Audioscrobbler server: %s", url);
889
890 audioscrobbler->priv->status = HANDSHAKING;
891 rb_audioscrobbler_statistics_changed (audioscrobbler);
892
893 rb_audioscrobbler_perform (audioscrobbler,
894 url,
895 NULL,
896 rb_audioscrobbler_do_handshake_cb);
897
898 g_free (url);
899 }
900
901
902 static void
903 rb_audioscrobbler_do_handshake_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
904 {
905 RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER(user_data);
906
907 rb_debug ("Handshake response");
908 rb_audioscrobbler_parse_response (audioscrobbler, msg, TRUE);
909 rb_audioscrobbler_statistics_changed (audioscrobbler);
910
911 switch (audioscrobbler->priv->status) {
912 case STATUS_OK:
913 audioscrobbler->priv->handshake = TRUE;
914 audioscrobbler->priv->handshake_delay = INITIAL_HANDSHAKE_DELAY;
915 audioscrobbler->priv->failures = 0;
916 break;
917 default:
918 rb_debug ("Handshake failed");
919 ++audioscrobbler->priv->failures;
920
921 audioscrobbler->priv->handshake_next = time (NULL) + audioscrobbler->priv->handshake_delay;
922
923 audioscrobbler->priv->handshake_delay *= 2;
924 if (audioscrobbler->priv->handshake_delay > MAX_HANDSHAKE_DELAY) {
925 audioscrobbler->priv->handshake_delay = MAX_HANDSHAKE_DELAY;
926 }
927 rb_debug ("handshake delay is now %d minutes", audioscrobbler->priv->handshake_delay/60);
928 break;
929 }
930
931 g_idle_add ((GSourceFunc) idle_unref_cb, audioscrobbler);
932 }
933
934 static gchar *
935 rb_audioscrobbler_build_post_data (RBAudioscrobbler *audioscrobbler)
936 {
937 g_return_val_if_fail (!g_queue_is_empty (audioscrobbler->priv->queue), NULL);
938
939 gchar *post_data = g_strdup_printf ("s=%s", audioscrobbler->priv->sessionid);
940 int i = 0;
941 do {
942 AudioscrobblerEntry *entry;
943 AudioscrobblerEncodedEntry *encoded;
944 gchar *new;
945
946 /* remove first queue entry */
947 entry = g_queue_pop_head (audioscrobbler->priv->queue);
948 encoded = rb_audioscrobbler_entry_encode (entry);
949 new = g_strdup_printf ("%s&a[%d]=%s&t[%d]=%s&b[%d]=%s&m[%d]=%s&l[%d]=%d&i[%d]=%s&o[%d]=%s&n[%d]=%s&r[%d]=",
950 post_data,
951 i, encoded->artist,
952 i, encoded->title,
953 i, encoded->album,
954 i, encoded->mbid,
955 i, encoded->length,
956 i, encoded->timestamp,
957 i, encoded->source,
958 i, encoded->track,
959 i);
960 rb_audioscrobbler_encoded_entry_free (encoded);
961 g_free (post_data);
962 post_data = new;
963
964 /* add to submission list */
965 g_queue_push_tail (audioscrobbler->priv->submission, entry);
966 i++;
967 } while ((!g_queue_is_empty (audioscrobbler->priv->queue)) && (i < MAX_SUBMIT_SIZE));
968
969 return post_data;
970 }
971
972 static void
973 rb_audioscrobbler_submit_queue (RBAudioscrobbler *audioscrobbler)
974 {
975 if (audioscrobbler->priv->sessionid != NULL) {
976 gchar *post_data;
977
978 post_data = rb_audioscrobbler_build_post_data (audioscrobbler);
979
980 rb_debug ("Submitting queue to Audioscrobbler");
981 rb_audioscrobbler_print_queue (audioscrobbler, TRUE);
982
983 rb_audioscrobbler_perform (audioscrobbler,
984 audioscrobbler->priv->submit_url,
985 post_data,
986 rb_audioscrobbler_submit_queue_cb);
987 /* libsoup will free post_data when the request is finished */
988 }
989 }
990
991 static void
992 rb_g_queue_concat (GQueue *q1, GQueue *q2)
993 {
994 GList *elem;
995
996 while (!g_queue_is_empty (q2)) {
997 elem = g_queue_pop_head_link (q2);
998 g_queue_push_tail_link (q1, elem);
999 }
1000 }
1001
1002 static void
1003 rb_audioscrobbler_submit_queue_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
1004 {
1005 RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (user_data);
1006
1007 rb_debug ("Submission response");
1008 rb_audioscrobbler_parse_response (audioscrobbler, msg, FALSE);
1009
1010 if (audioscrobbler->priv->status == STATUS_OK) {
1011 rb_debug ("Queue submitted successfully");
1012 rb_audioscrobbler_free_queue_entries (audioscrobbler, &audioscrobbler->priv->submission);
1013 audioscrobbler->priv->submission = g_queue_new ();
1014 rb_audioscrobbler_save_queue (audioscrobbler);
1015
1016 audioscrobbler->priv->submit_count += audioscrobbler->priv->queue_count;
1017 audioscrobbler->priv->queue_count = 0;
1018
1019 g_free (audioscrobbler->priv->submit_time);
1020 audioscrobbler->priv->submit_time = rb_utf_friendly_time (time (NULL));
1021 } else {
1022 ++audioscrobbler->priv->failures;
1023
1024 /* add failed submission entries back to queue */
1025 rb_g_queue_concat (audioscrobbler->priv->submission,
1026 audioscrobbler->priv->queue);
1027 g_assert (g_queue_is_empty (audioscrobbler->priv->queue));
1028 g_queue_free (audioscrobbler->priv->queue);
1029 audioscrobbler->priv->queue = audioscrobbler->priv->submission;
1030 audioscrobbler->priv->submission = g_queue_new ();;
1031 rb_audioscrobbler_save_queue (audioscrobbler);
1032
1033 rb_audioscrobbler_print_queue (audioscrobbler, FALSE);
1034
1035 if (audioscrobbler->priv->failures >= 3) {
1036 rb_debug ("Queue submission has failed %d times; caching tracks locally",
1037 audioscrobbler->priv->failures);
1038 g_free (audioscrobbler->priv->status_msg);
1039
1040 audioscrobbler->priv->handshake = FALSE;
1041 audioscrobbler->priv->status = GIVEN_UP;
1042 audioscrobbler->priv->status_msg = NULL;
1043 } else {
1044 rb_debug ("Queue submission failed %d times", audioscrobbler->priv->failures);
1045 }
1046 }
1047
1048 rb_audioscrobbler_statistics_changed (audioscrobbler);
1049 g_idle_add ((GSourceFunc) idle_unref_cb, audioscrobbler);
1050 }
1051
1052 static void
1053 rb_audioscrobbler_song_changed_cb (RBShellPlayer *player,
1054 RhythmDBEntry *entry,
1055 RBAudioscrobbler *audioscrobbler)
1056 {
1057 gboolean got_time;
1058 guint playing_time;
1059
1060 if (audioscrobbler->priv->currently_playing != NULL) {
1061 rb_audioscrobbler_entry_free (audioscrobbler->priv->currently_playing);
1062 audioscrobbler->priv->currently_playing = NULL;
1063 }
1064
1065 if (entry == NULL) {
1066 rb_debug ("called with no playing entry");
1067 return;
1068 }
1069 rb_debug ("new entry: %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
1070
1071 got_time = rb_shell_player_get_playing_time (audioscrobbler->priv->shell_player,
1072 &playing_time,
1073 NULL);
1074 if (got_time) {
1075 audioscrobbler->priv->current_elapsed = (int) playing_time;
1076 } else {
1077 rb_debug ("didn't get playing time; assuming 0");
1078 audioscrobbler->priv->current_elapsed = 0;
1079 }
1080
1081 if (rb_audioscrobbler_is_queueable (entry) && (got_time == FALSE || playing_time < 15)) {
1082 AudioscrobblerEntry *as_entry;
1083
1084 /* even if it's the same song, it's being played again from
1085 * the start so we can queue it again.
1086 */
1087 as_entry = rb_audioscrobbler_entry_create (entry, audioscrobbler->priv->service);
1088 as_entry->play_time = time (NULL);
1089 audioscrobbler->priv->currently_playing = as_entry;
1090 audioscrobbler->priv->now_playing_updated = FALSE;
1091 }
1092 }
1093
1094
1095 /* Queue functions: */
1096 static gboolean
1097 rb_audioscrobbler_load_queue (RBAudioscrobbler *audioscrobbler)
1098 {
1099 char *pathname;
1100 GFile *file;
1101 GError *error = NULL;
1102 char *data;
1103 char *start;
1104 char *end;
1105 gsize size;
1106
1107 /* we don't really care about errors enough to report them here */
1108 pathname = g_build_filename (rb_user_data_dir (),
1109 "audioscrobbler",
1110 "submission-queues",
1111 rb_audioscrobbler_service_get_name (audioscrobbler->priv->service),
1112 audioscrobbler->priv->username,
1113 NULL);
1114 file = g_file_new_for_path (pathname);
1115 rb_debug ("loading Audioscrobbler queue from \"%s\"", pathname);
1116 g_free (pathname);
1117
1118 if (g_file_load_contents (file, NULL, &data, &size, NULL, &error) == FALSE) {
1119 rb_debug ("unable to load audioscrobbler queue: %s", error->message);
1120 g_error_free (error);
1121 return FALSE;
1122 }
1123
1124 start = data;
1125 while (start < (data + size)) {
1126 AudioscrobblerEntry *entry;
1127
1128 /* find the end of the line, to terminate the string */
1129 end = g_utf8_strchr (start, -1, '\n');
1130 if (end == NULL)
1131 break;
1132 *end = 0;
1133
1134 entry = rb_audioscrobbler_entry_load_from_string (start);
1135 if (entry) {
1136 g_queue_push_tail (audioscrobbler->priv->queue,
1137 entry);
1138 audioscrobbler->priv->queue_count++;
1139 }
1140
1141 start = end + 1;
1142 }
1143
1144 g_free (data);
1145 return TRUE;
1146 }
1147
1148 static gboolean
1149 rb_audioscrobbler_save_queue (RBAudioscrobbler *audioscrobbler)
1150 {
1151 char *pathname;
1152 char *uri;
1153 GFile *file;
1154 GError *error = NULL;
1155 GList *l;
1156 GString *str;
1157
1158 if (!audioscrobbler->priv->queue_changed) {
1159 return TRUE;
1160 }
1161
1162 str = g_string_new ("");
1163 for (l = audioscrobbler->priv->queue->head; l != NULL; l = g_list_next (l)) {
1164 AudioscrobblerEntry *entry;
1165
1166 entry = (AudioscrobblerEntry *) l->data;
1167 rb_audioscrobbler_entry_save_to_string (str, entry);
1168 }
1169
1170 /* we don't really care about errors enough to report them here */
1171 pathname = g_build_filename (rb_user_data_dir (),
1172 "audioscrobbler",
1173 "submission-queues",
1174 rb_audioscrobbler_service_get_name (audioscrobbler->priv->service),
1175 audioscrobbler->priv->username,
1176 NULL);
1177 rb_debug ("Saving Audioscrobbler queue to \"%s\"", pathname);
1178
1179 uri = g_filename_to_uri (pathname, NULL, NULL);
1180 rb_uri_create_parent_dirs (uri, &error);
1181
1182 file = g_file_new_for_path (pathname);
1183 g_free (pathname);
1184 g_free (uri);
1185
1186 error = NULL;
1187 g_file_replace_contents (file,
1188 str->str, str->len,
1189 NULL,
1190 FALSE,
1191 G_FILE_CREATE_NONE,
1192 NULL,
1193 NULL,
1194 &error);
1195 g_string_free (str, TRUE);
1196
1197 if (error == NULL) {
1198 audioscrobbler->priv->queue_changed = FALSE;
1199 return TRUE;
1200 } else {
1201 rb_debug ("error saving audioscrobbler queue: %s",
1202 error->message);
1203 g_error_free (error);
1204 return FALSE;
1205 }
1206 }
1207
1208 static void
1209 rb_audioscrobbler_print_queue (RBAudioscrobbler *audioscrobbler, gboolean submission)
1210 {
1211 GList *l;
1212 AudioscrobblerEntry *entry;
1213 int i = 0;
1214
1215 if (submission) {
1216 l = audioscrobbler->priv->submission->head;
1217 rb_debug ("Audioscrobbler submission (%d entries): ",
1218 g_queue_get_length (audioscrobbler->priv->submission));
1219
1220 } else {
1221 l = audioscrobbler->priv->queue->head;
1222 rb_debug ("Audioscrobbler queue (%d entries): ",
1223 g_queue_get_length (audioscrobbler->priv->queue));
1224 }
1225
1226 for (; l != NULL; l = g_list_next (l)) {
1227 entry = (AudioscrobblerEntry *) l->data;
1228 rb_audioscrobbler_entry_debug (entry, ++i);
1229 }
1230 }
1231
1232 static void
1233 rb_audioscrobbler_free_queue_entries (RBAudioscrobbler *audioscrobbler, GQueue **queue)
1234 {
1235 g_queue_foreach (*queue, (GFunc) rb_audioscrobbler_entry_free, NULL);
1236 g_queue_free (*queue);
1237 *queue = NULL;
1238
1239 audioscrobbler->priv->queue_changed = TRUE;
1240 }
1241
1242 static void
1243 rb_audioscrobbler_nowplaying (RBAudioscrobbler *audioscrobbler, AudioscrobblerEntry *entry)
1244 {
1245 AudioscrobblerEncodedEntry *encoded;
1246 gchar *post_data;
1247
1248 if (audioscrobbler->priv->handshake) {
1249 encoded = rb_audioscrobbler_entry_encode (entry);
1250
1251 post_data = g_strdup_printf ("s=%s&a=%s&t=%s&b=%s&l=%d&n=%s&m=%s",
1252 audioscrobbler->priv->sessionid,
1253 encoded->artist,
1254 encoded->title,
1255 encoded->album,
1256 encoded->length,
1257 encoded->track,
1258 encoded->mbid);
1259
1260 rb_audioscrobbler_perform (audioscrobbler,
1261 audioscrobbler->priv->nowplaying_url,
1262 post_data,
1263 rb_audioscrobbler_nowplaying_cb);
1264
1265 rb_audioscrobbler_encoded_entry_free (encoded);
1266 }
1267 }
1268
1269 static void
1270 rb_audioscrobbler_nowplaying_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
1271 {
1272 RBAudioscrobbler *audioscrobbler = RB_AUDIOSCROBBLER (user_data);
1273 rb_debug ("Now playing response");
1274 rb_audioscrobbler_parse_response (audioscrobbler, msg, FALSE);
1275
1276 if (audioscrobbler->priv->status == STATUS_OK) {
1277 rb_debug("Submission success!");
1278 } else {
1279 rb_debug("Error submitting now playing information.");
1280 }
1281
1282 g_idle_add ((GSourceFunc) idle_unref_cb, audioscrobbler);
1283 }
1284
1285 void
1286 _rb_audioscrobbler_register_type (GTypeModule *module)
1287 {
1288 rb_audioscrobbler_register_type (module);
1289 }