Updated Finnish translation
[rhythmbox.git] / plugins / audioscrobbler / rb-lastfm-source.c
blobc145227bd04dcef25c186fa1622ede1078cac08a
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of last.fm station source object
5 * Copyright (C) 2006 Matt Novenstern <fisxoj@gmail.com>
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.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 /* The author would like to extend thanks to Ian Holmes, author of Last Exit,
24 * an alternative last.fm player written in C#, the code of which was
25 * extraordinarily useful in the creation of this code
28 /* TODO List
29 * - if subscriber, make user radio (low priority)
30 * - "recommendation radio" with percentage setting (0=obscure, 100=popular)
31 * - watch username gconf entries, create/update neighbour station
35 #include <config.h>
37 #include <string.h>
39 #include <glib/gi18n.h>
40 #include <gtk/gtk.h>
41 #include <glade/glade.h>
43 #include <gconf/gconf-value.h>
45 #include <libsoup/soup.h>
46 #include <libsoup/soup-uri.h>
48 #include "md5.h"
50 #include "eel-gconf-extensions.h"
52 #include "rb-proxy-config.h"
53 #include "rb-preferences.h"
55 #include "rb-audioscrobbler.h"
56 #include "rb-lastfm-source.h"
58 #include "rhythmdb-query-model.h"
59 #include "rb-glade-helpers.h"
60 #include "rb-stock-icons.h"
61 #include "rb-entry-view.h"
62 #include "rb-property-view.h"
63 #include "rb-util.h"
64 #include "rb-file-helpers.h"
65 #include "rb-preferences.h"
66 #include "rb-dialog.h"
67 #include "rb-station-properties-dialog.h"
68 #include "rb-new-station-dialog.h"
69 #include "rb-debug.h"
70 #include "eel-gconf-extensions.h"
71 #include "rb-shell-player.h"
73 #define LASTFM_URL "http://ws.audioscrobbler.com"
74 #define PLATFORM_STRING "linux"
75 #define RB_LASTFM_VERSION "1.1.1"
76 #define EXTRA_URI_ENCODE_CHARS "&+"
79 static void rb_lastfm_source_class_init (RBLastfmSourceClass *klass);
80 static void rb_lastfm_source_init (RBLastfmSource *source);
81 static GObject *rb_lastfm_source_constructor (GType type, guint n_construct_properties,
82 GObjectConstructParam *construct_properties);
83 static void rb_lastfm_source_finalize (GObject *object);
84 static void rb_lastfm_source_set_property (GObject *object,
85 guint prop_id,
86 const GValue *value,
87 GParamSpec *pspec);
88 static void rb_lastfm_source_get_property (GObject *object,
89 guint prop_id,
90 GValue *value,
91 GParamSpec *pspec);
93 static void rb_lastfm_source_songs_view_sort_order_changed_cb (RBEntryView *view,
94 RBLastfmSource *source);
95 static void rb_lastfm_source_do_query (RBLastfmSource *source);
97 /* source-specific methods */
98 static void rb_lastfm_source_do_handshake (RBLastfmSource *source);
99 static char* rb_lastfm_source_get_playback_uri (RhythmDBEntry *entry, gpointer data);
100 static void rb_lastfm_perform (RBLastfmSource *lastfm,
101 const char *url,
102 char *post_data, /* this takes ownership */
103 SoupMessageCallbackFn response_handler);
104 static void rb_lastfm_message_cb (SoupMessage *req, gpointer user_data);
105 static void rb_lastfm_change_station (RBLastfmSource *source, const char *station);
107 static void rb_lastfm_proxy_config_changed_cb (RBProxyConfig *config,
108 RBLastfmSource *source);
109 static void rb_lastfm_source_drag_cb (GtkWidget *widget,
110 GdkDragContext *dc,
111 gint x, gint y,
112 GtkSelectionData *selection_data,
113 guint info, guint time,
114 RBLastfmSource *source);
116 static void rb_lastfm_source_dispose (GObject *object);
118 /* RBSource implementation methods */
119 static void impl_delete (RBSource *asource);
120 static GList *impl_get_ui_actions (RBSource *source);
121 static RBEntryView *impl_get_entry_view (RBSource *asource);
122 static void impl_get_status (RBSource *asource, char **text, char **progress_text, float *progress);
123 static RBSourceEOFType impl_handle_eos (RBSource *asource);
124 static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data);
125 static void impl_activate (RBSource *source);
127 static void rb_lastfm_source_new_station (char *uri, char *title, RBLastfmSource *source);
128 static void rb_lastfm_source_skip_track (GtkAction *action, RBLastfmSource *source);
129 static void rb_lastfm_source_love_track (GtkAction *action, RBLastfmSource *source);
130 static void rb_lastfm_source_ban_track (GtkAction *action, RBLastfmSource *source);
131 static char *rb_lastfm_source_title_from_uri (char *uri);
132 static void rb_lastfm_source_add_station_cb (GtkButton *button, gpointer *data);
134 static void rb_lastfm_source_new_song_cb (GObject *player_backend, gpointer data, RBLastfmSource *source);
135 static void rb_lastfm_song_changed_cb (RBShellPlayer *player, RhythmDBEntry *entry, RBLastfmSource *source);
137 static GValue *streaming_title_request_cb (RhythmDB *db,
138 RhythmDBEntry *entry,
139 RBLastfmSource *source);
140 static GValue *streaming_album_request_cb (RhythmDB *db,
141 RhythmDBEntry *entry,
142 RBLastfmSource *source);
143 static GValue *streaming_artist_request_cb (RhythmDB *db,
144 RhythmDBEntry *entry,
145 RBLastfmSource *source);
146 static void extra_metadata_gather_cb (RhythmDB *db,
147 RhythmDBEntry *entry,
148 GHashTable *data,
149 RBLastfmSource *source);
150 static void playing_source_changed_cb (RBShellPlayer *player,
151 RBSource *source,
152 RBLastfmSource *lastfm_source);
153 #ifdef HAVE_GSTREAMER_0_10
154 /* can't be bothered creating a whole header file just for this: */
155 GType rb_lastfm_src_get_type (void);
156 #endif
158 struct RBLastfmSourcePrivate
160 GtkWidget *vbox;
161 GtkWidget *paned;
162 GtkWidget *vbox2;
163 GtkWidget *hbox;
164 /*GtkWidget *tuner;*/
165 GtkWidget *txtbox;
166 GtkWidget *gobutton;
167 GtkWidget *typecombo;
168 GtkWidget *label;
169 RhythmDB *db;
171 GtkActionGroup *action_group;
173 RBEntryView *stations;
175 RBShellPlayer *shell_player;
176 RhythmDBEntryType entry_type;
177 char *session;
179 gboolean subscriber;
180 char *base_url;
181 char *base_path;
182 char *stream_url;
183 gboolean framehack;
184 char *update_url;
185 gboolean banned;
186 gboolean connected;
188 /*RhythmDBEntry *pending_entry;*/
189 char *streaming_title;
190 char *streaming_artist;
191 char *streaming_album;
193 enum {
194 OK = 0,
195 COMMUNICATING,
196 FAILED,
197 NO_ARTIST,
198 BANNED
199 } status;
201 SoupSession *soup_session;
202 RBProxyConfig *proxy_config;
204 gint buffering_id;
205 guint buffering;
208 G_DEFINE_TYPE (RBLastfmSource, rb_lastfm_source, RB_TYPE_SOURCE);
209 #define RB_LASTFM_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LASTFM_SOURCE, RBLastfmSourcePrivate))
211 enum
213 PROP_0,
214 PROP_ENTRY_TYPE,
215 PROP_PROXY_CONFIG
218 static GtkActionEntry rb_lastfm_source_actions [] =
220 { "LastfmSkipSong", GTK_STOCK_MEDIA_FORWARD, N_("Next Song"), NULL,
221 N_("Skip the current track"),
222 G_CALLBACK (rb_lastfm_source_skip_track) },
223 { "LastfmLoveSong", GTK_STOCK_ADD, N_("Love Song"), NULL,
224 N_("Mark this song as loved"),
225 G_CALLBACK (rb_lastfm_source_love_track) },
226 { "LastfmBanSong", GTK_STOCK_CANCEL, N_("Ban Song"), NULL,
227 N_("Ban the current track from being played again"),
228 G_CALLBACK (rb_lastfm_source_ban_track) }
231 static const GtkTargetEntry lastfm_drag_types[] = {
232 { "text/plain", 0, 0 },
233 { "_NETSCAPE_URL", 0, 1 }
236 static void
237 rb_lastfm_source_class_init (RBLastfmSourceClass *klass)
239 GObjectClass *object_class = G_OBJECT_CLASS (klass);
240 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
242 object_class->finalize = rb_lastfm_source_finalize;
243 object_class->dispose = rb_lastfm_source_dispose;
244 object_class->constructor = rb_lastfm_source_constructor;
246 object_class->set_property = rb_lastfm_source_set_property;
247 object_class->get_property = rb_lastfm_source_get_property;
249 source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
250 source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
251 source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
252 source_class->impl_delete = impl_delete;
253 source_class->impl_get_entry_view = impl_get_entry_view;
254 source_class->impl_get_status = impl_get_status;
255 source_class->impl_get_ui_actions = impl_get_ui_actions;
256 source_class->impl_handle_eos = impl_handle_eos;
257 source_class->impl_receive_drag = impl_receive_drag;
258 source_class->impl_activate = impl_activate;
259 source_class->impl_try_playlist = (RBSourceFeatureFunc) rb_true_function;
261 g_object_class_install_property (object_class,
262 PROP_ENTRY_TYPE,
263 g_param_spec_boxed ("entry-type",
264 "Entry type",
265 "Type of the entries which should be displayed by this source",
266 RHYTHMDB_TYPE_ENTRY_TYPE,
267 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
268 g_object_class_install_property (object_class,
269 PROP_PROXY_CONFIG,
270 g_param_spec_object ("proxy-config",
271 "RBProxyConfig",
272 "RBProxyConfig object",
273 RB_TYPE_PROXY_CONFIG,
274 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
275 g_type_class_add_private (klass, sizeof (RBLastfmSourcePrivate));
277 #ifdef HAVE_GSTREAMER_0_10
278 rb_lastfm_src_get_type ();
279 #endif
282 static void
283 rb_lastfm_source_init (RBLastfmSource *source)
285 gint size;
286 GdkPixbuf *pixbuf;
288 source->priv = RB_LASTFM_SOURCE_GET_PRIVATE (source);
290 source->priv->vbox = gtk_vbox_new (FALSE, 5);
292 gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
294 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
295 pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
296 "stock_channel",
297 size,
298 0, NULL);
299 rb_source_set_pixbuf (RB_SOURCE (source), pixbuf);
300 if (pixbuf != NULL) {
301 g_object_unref (pixbuf);
305 static void
306 rb_lastfm_source_finalize (GObject *object)
308 RBLastfmSource *source;
310 g_return_if_fail (object != NULL);
311 g_return_if_fail (RB_IS_LASTFM_SOURCE (object));
313 source = RB_LASTFM_SOURCE (object);
315 g_return_if_fail (source->priv != NULL);
317 rb_debug ("finalizing lastfm source");
319 if (source->priv->db) {
320 g_object_unref (source->priv->db);
321 source->priv->db = NULL;
324 g_free (source->priv->streaming_title);
325 g_free (source->priv->streaming_artist);
326 g_free (source->priv->streaming_album);
328 g_object_unref (G_OBJECT (source->priv->proxy_config));
330 G_OBJECT_CLASS (rb_lastfm_source_parent_class)->finalize (object);
333 static GObject *
334 rb_lastfm_source_constructor (GType type, guint n_construct_properties,
335 GObjectConstructParam *construct_properties)
337 RBLastfmSource *source;
338 RBLastfmSourceClass *klass;
339 RBShell *shell;
340 GObject *player_backend;
342 klass = RB_LASTFM_SOURCE_CLASS (g_type_class_peek (RB_TYPE_LASTFM_SOURCE));
344 source = RB_LASTFM_SOURCE (G_OBJECT_CLASS (rb_lastfm_source_parent_class)
345 ->constructor (type, n_construct_properties, construct_properties));
347 g_object_get (G_OBJECT (source), "shell", &shell, NULL);
348 g_object_get (G_OBJECT (shell),
349 "db", &source->priv->db,
350 "shell-player", &source->priv->shell_player,
351 NULL);
352 g_object_unref (G_OBJECT (shell));
354 /* Set up station tuner */
355 /*source->priv->tuner = gtk_vbox_new (FALSE, 5); */
356 source->priv->vbox2 = gtk_vbox_new (FALSE, 5);
357 source->priv->hbox = gtk_hbox_new (FALSE, 5);
359 source->priv->label = gtk_label_new (_("Enter the artist or global tag to build a radio station out of:"));
360 g_object_set (source->priv->label, "xalign", 0.0, NULL);
362 source->priv->gobutton = gtk_button_new_with_label (_("Add"));
363 g_signal_connect_object (G_OBJECT (source->priv->gobutton),
364 "clicked",
365 G_CALLBACK (rb_lastfm_source_add_station_cb),
366 source, 0);
367 source->priv->typecombo = gtk_combo_box_new_text ();
368 gtk_combo_box_append_text (GTK_COMBO_BOX (source->priv->typecombo), _("Artist"));
369 gtk_combo_box_append_text (GTK_COMBO_BOX (source->priv->typecombo), _("Tag"));
370 gtk_combo_box_set_active (GTK_COMBO_BOX (source->priv->typecombo), 0);
372 source->priv->txtbox = gtk_entry_new ();
374 gtk_box_pack_end_defaults (GTK_BOX (source->priv->hbox),
375 GTK_WIDGET (source->priv->gobutton));
377 gtk_box_pack_end_defaults (GTK_BOX (source->priv->hbox),
378 GTK_WIDGET (source->priv->txtbox));
380 gtk_box_pack_start_defaults (GTK_BOX (source->priv->hbox),
381 GTK_WIDGET (source->priv->typecombo));
383 gtk_box_pack_end_defaults (GTK_BOX (source->priv->vbox2),
384 GTK_WIDGET (source->priv->hbox));
386 gtk_box_pack_end_defaults (GTK_BOX (source->priv->vbox2),
387 GTK_WIDGET (source->priv->label));
389 /* set up stations view */
390 source->priv->stations = rb_entry_view_new (source->priv->db,
391 G_OBJECT (source->priv->shell_player),
392 NULL,
393 FALSE, FALSE);
394 /*rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_TITLE, TRUE);*/
395 rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_GENRE, TRUE);
396 rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_RATING, TRUE);
397 rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_LAST_PLAYED, TRUE);
398 g_signal_connect_object (G_OBJECT (source->priv->stations),
399 "sort-order-changed",
400 G_CALLBACK (rb_lastfm_source_songs_view_sort_order_changed_cb),
401 source, 0);
403 /* Drag and drop URIs */
404 g_signal_connect_object (G_OBJECT (source->priv->stations),
405 "drag_data_received",
406 G_CALLBACK (rb_lastfm_source_drag_cb),
407 source, 0);
408 g_signal_connect_object (G_OBJECT (source->priv->shell_player),
409 "playing-song-changed",
410 G_CALLBACK (rb_lastfm_song_changed_cb),
411 source, 0);
413 gtk_drag_dest_set (GTK_WIDGET (source->priv->stations),
414 GTK_DEST_DEFAULT_ALL,
415 lastfm_drag_types, 2,
416 GDK_ACTION_COPY | GDK_ACTION_MOVE);
418 /* Pack the vbox */
419 /*gtk_paned_pack1 (GTK_PANED (source->priv->paned),
420 GTK_WIDGET (source->priv->tuner), FALSE, FALSE); */
422 /*source->priv->paned = gtk_vpaned_new ();
423 gtk_paned_pack2 (GTK_PANED (source->priv->paned),
424 GTK_WIDGET (source->priv->stations), TRUE, FALSE); */
426 gtk_box_pack_start (GTK_BOX (source->priv->vbox), GTK_WIDGET (source->priv->vbox2), FALSE, FALSE, 5);
427 gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), GTK_WIDGET (source->priv->stations));
430 gtk_widget_show_all (GTK_WIDGET (source));
433 source->priv->action_group = _rb_source_register_action_group (RB_SOURCE (source),
434 "LastfmActions",
435 rb_lastfm_source_actions,
436 G_N_ELEMENTS (rb_lastfm_source_actions),
437 source);
439 rb_lastfm_source_do_query (source);
441 g_signal_connect_object (G_OBJECT (source->priv->db),
442 "entry-extra-metadata-request::" RHYTHMDB_PROP_STREAM_SONG_TITLE,
443 G_CALLBACK (streaming_title_request_cb),
444 source, 0);
446 g_signal_connect_object (G_OBJECT (source->priv->db),
447 "entry-extra-metadata-request::" RHYTHMDB_PROP_STREAM_SONG_ARTIST,
448 G_CALLBACK (streaming_artist_request_cb),
449 source, 0);
451 g_signal_connect_object (G_OBJECT (source->priv->db),
452 "entry-extra-metadata-request::" RHYTHMDB_PROP_STREAM_SONG_ALBUM,
453 G_CALLBACK (streaming_album_request_cb),
454 source, 0);
456 g_signal_connect_object (G_OBJECT (source->priv->db),
457 "entry-extra-metadata-gather",
458 G_CALLBACK (extra_metadata_gather_cb),
459 source, 0);
461 g_object_get (source->priv->shell_player, "player", &player_backend, NULL);
462 g_signal_connect_object (player_backend,
463 "event::rb-lastfm-new-song",
464 G_CALLBACK (rb_lastfm_source_new_song_cb),
465 source,
467 source->priv->buffering = -1;
468 g_signal_connect_object (source->priv->shell_player, "playing-source-changed",
469 G_CALLBACK (playing_source_changed_cb),
470 source, 0);
472 return G_OBJECT (source);
475 static void
476 rb_lastfm_source_set_property (GObject *object,
477 guint prop_id,
478 const GValue *value,
479 GParamSpec *pspec)
481 RBLastfmSource *source = RB_LASTFM_SOURCE (object);
483 switch (prop_id) {
484 case PROP_ENTRY_TYPE:
485 source->priv->entry_type = g_value_get_boxed (value);
486 break;
487 case PROP_PROXY_CONFIG:
488 source->priv->proxy_config = g_value_get_object (value);
489 g_object_ref (G_OBJECT (source->priv->proxy_config));
490 g_signal_connect_object (G_OBJECT (source->priv->proxy_config),
491 "config-changed",
492 G_CALLBACK (rb_lastfm_proxy_config_changed_cb),
493 source, 0);
494 break;
495 default:
496 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
497 break;
501 static void
502 rb_lastfm_source_get_property (GObject *object,
503 guint prop_id,
504 GValue *value,
505 GParamSpec *pspec)
507 RBLastfmSource *source = RB_LASTFM_SOURCE (object);
509 switch (prop_id) {
510 case PROP_ENTRY_TYPE:
511 g_value_set_boxed (value, source->priv->entry_type);
512 break;
513 default:
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
515 break;
519 static gchar *
520 mkmd5 (char *string)
522 md5_state_t md5state;
523 guchar md5pword[16];
524 gchar md5_response[33];
526 int j = 0;
528 memset (md5_response, 0, sizeof (md5_response));
530 md5_init (&md5state);
531 md5_append (&md5state, (unsigned char*)string, strlen (string));
532 md5_finish (&md5state, md5pword);
534 for (j = 0; j < 16; j++) {
535 char a[3];
536 sprintf (a, "%02x", md5pword[j]);
537 md5_response[2*j] = a[0];
538 md5_response[2*j+1] = a[1];
541 return (g_strdup (md5_response));
544 RBSource *
545 rb_lastfm_source_new (RBShell *shell)
547 RBSource *source;
548 RBProxyConfig *proxy_config;
549 RhythmDBEntryType entry_type;
550 char *uri;
551 RhythmDB *db;
552 char *username;
554 g_object_get (G_OBJECT (shell), "db", &db, NULL);
556 /* register entry type if it's not already registered */
557 entry_type = rhythmdb_entry_type_get_by_name (db, "lastfm-station");
558 if (entry_type == RHYTHMDB_ENTRY_TYPE_INVALID) {
559 entry_type = rhythmdb_entry_register_type (db, "lastfm-station");
560 entry_type->save_to_disk = TRUE;
561 entry_type->can_sync_metadata = (RhythmDBEntryCanSyncFunc) rb_true_function;
562 entry_type->sync_metadata = (RhythmDBEntrySyncFunc) rb_null_function;
563 entry_type->get_playback_uri = (RhythmDBEntryStringFunc) rb_lastfm_source_get_playback_uri;
566 g_object_get (G_OBJECT (shell), "proxy-config", &proxy_config, NULL);
568 source = RB_SOURCE (g_object_new (RB_TYPE_LASTFM_SOURCE,
569 "name", _("Last.fm"),
570 "shell", shell,
571 "entry-type", entry_type,
572 "proxy-config", proxy_config,
573 NULL));
574 rb_shell_register_entry_type_for_source (shell, source, entry_type);
576 entry_type->get_playback_uri_data = source;
578 /* create default neighbour radio station */
579 username = eel_gconf_get_string (CONF_AUDIOSCROBBLER_USERNAME);
580 if (username != NULL) {
581 RhythmDBEntry *entry;
583 uri = g_strdup_printf ("lastfm://user/%s/neighbours", username);
584 entry = rhythmdb_entry_lookup_by_location (db, uri);
585 if (entry == NULL) {
586 rb_lastfm_source_new_station (uri, _("Neighbour Radio"), RB_LASTFM_SOURCE (source));
587 } else {
588 rhythmdb_entry_unref (entry);
590 g_free (uri);
591 g_free (username);
594 g_object_unref (db);
595 g_object_unref (proxy_config);
596 return source;
599 static GList*
600 impl_get_ui_actions (RBSource *source)
602 GList *actions = NULL;
604 actions = g_list_prepend (actions, g_strdup ("LastfmLoveSong"));
605 actions = g_list_prepend (actions, g_strdup ("LastfmBanSong"));
606 actions = g_list_prepend (actions, g_strdup ("LastfmSkipSong"));
608 return actions;
611 static RBEntryView *
612 impl_get_entry_view (RBSource *asource)
614 RBLastfmSource *source = RB_LASTFM_SOURCE (asource);
616 return source->priv->stations;
619 static RBSourceEOFType
620 impl_handle_eos (RBSource *asource)
622 return RB_SOURCE_EOF_RETRY;
625 static void
626 impl_get_status (RBSource *asource, char **text, char **progress_text, float *progress)
628 RBLastfmSource *source = RB_LASTFM_SOURCE (asource);
630 if (source->priv->buffering != -1) {
631 *progress = ((float)source->priv->buffering)/100;
632 *text = g_strdup (_("Buffering"));
633 } else {
634 switch (source->priv->status) {
635 case NO_ARTIST:
636 *text = g_strdup (_("No such artist. Check your spelling"));
637 break;
639 case FAILED:
640 *text = g_strdup (_("Handshake failed"));
641 break;
643 case BANNED:
644 *text = g_strdup (_("The server marked you as banned"));
645 break;
647 case COMMUNICATING:
648 case OK:
650 RhythmDBQueryModel *model;
651 guint num_entries;
653 g_object_get (asource, "query-model", &model, NULL);
654 num_entries = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL);
655 g_object_unref (model);
657 *text = g_strdup_printf (ngettext ("%d station", "%d stations", num_entries), num_entries);
658 break;
664 static void
665 impl_delete (RBSource *asource)
667 RBLastfmSource *source = RB_LASTFM_SOURCE (asource);
668 GList *l;
670 for (l = rb_entry_view_get_selected_entries (source->priv->stations); l != NULL; l = g_list_next (l)) {
671 rhythmdb_entry_delete (source->priv->db, l->data);
674 rhythmdb_commit (source->priv->db);
677 static void
678 rb_lastfm_source_songs_view_sort_order_changed_cb (RBEntryView *view,
679 RBLastfmSource *source)
681 rb_debug ("sort order changed");
683 rb_entry_view_resort_model (view);
686 static void
687 rb_lastfm_source_do_query (RBLastfmSource *source)
689 RhythmDBQueryModel *station_query_model;
690 GPtrArray *query;
692 query = rhythmdb_query_parse (source->priv->db,
693 RHYTHMDB_QUERY_PROP_EQUALS,
694 RHYTHMDB_PROP_TYPE,
695 source->priv->entry_type,
696 RHYTHMDB_QUERY_END);
697 station_query_model = rhythmdb_query_model_new_empty (source->priv->db);
698 rhythmdb_do_full_query_parsed (source->priv->db,
699 RHYTHMDB_QUERY_RESULTS (station_query_model),
700 query);
702 rhythmdb_query_free (query);
703 query = NULL;
705 rb_entry_view_set_model (source->priv->stations, station_query_model);
706 g_object_set (G_OBJECT (source), "query-model", station_query_model, NULL);
708 g_object_unref (G_OBJECT (station_query_model));
711 static void
712 rb_lastfm_source_do_handshake (RBLastfmSource *source)
714 char *password;
715 char *username;
716 char *md5password;
717 char *handshake_url;
719 if (source->priv->connected) {
720 return;
723 username = eel_gconf_get_string (CONF_AUDIOSCROBBLER_USERNAME);
724 if (username == NULL) {
725 rb_debug ("no last.fm username");
726 return;
729 password = eel_gconf_get_string (CONF_AUDIOSCROBBLER_PASSWORD);
730 if (password == NULL) {
731 rb_debug ("no last.fm password");
732 return;
735 md5password = mkmd5 (password);
736 g_free (password);
738 handshake_url = g_strdup_printf ("%s/radio/handshake.php?version=1.1.1&platform=linux&"
739 "username=%s&passwordmd5=%s&debug=0&partner=",
740 LASTFM_URL,
741 username,
742 md5password);
743 rb_debug ("Last.fm sending handshake");
744 g_object_ref (source);
745 rb_lastfm_perform (source, handshake_url, NULL, rb_lastfm_message_cb);
746 g_free (handshake_url);
747 g_free (username);
748 g_free (md5password);
751 static char *
752 rb_lastfm_source_get_playback_uri (RhythmDBEntry *entry, gpointer data)
754 char *location;
755 RBLastfmSource *source;
757 if (entry == NULL) {
758 rb_debug ("NULL entry");
759 return NULL;
762 source = RB_LASTFM_SOURCE (data);
763 if (source == NULL) {
764 rb_debug ("NULL source pointer");
765 return NULL;
769 if (!source->priv->connected) {
770 rb_debug ("not connected");
771 return NULL;
773 source = RB_LASTFM_SOURCE (data);
775 location = g_strdup_printf ("xrblastfm://%s", source->priv->stream_url + strlen("http://"));
776 rb_debug ("playback uri: %s", location);
777 return location;
780 static void
781 rb_lastfm_perform (RBLastfmSource *source,
782 const char *url,
783 char *post_data,
784 SoupMessageCallbackFn response_handler)
786 SoupMessage *msg;
787 msg = soup_message_new ("GET", url);
789 if (msg == NULL)
790 return;
792 soup_message_set_http_version (msg, SOUP_HTTP_1_1);
794 rb_debug ("Last.fm communicating with %s", url);
796 if (post_data != NULL) {
797 rb_debug ("POST data: %s", post_data);
798 soup_message_set_request (msg,
799 "application/x-www-form-urlencoded",
800 SOUP_BUFFER_SYSTEM_OWNED,
801 post_data,
802 strlen (post_data));
805 /* create soup session, if we haven't got one yet */
806 if (!source->priv->soup_session) {
807 SoupUri *uri;
809 uri = rb_proxy_config_get_libsoup_uri (source->priv->proxy_config);
810 source->priv->soup_session = soup_session_async_new_with_options (
811 "proxy-uri", uri,
812 NULL);
813 if (uri)
814 soup_uri_free (uri);
817 soup_session_queue_message (source->priv->soup_session,
818 msg,
819 (SoupMessageCallbackFn) response_handler,
820 source);
821 source->priv->status = COMMUNICATING;
822 rb_source_notify_status_changed (RB_SOURCE(source));
825 static void
826 rb_lastfm_message_cb (SoupMessage *req, gpointer user_data)
828 RBLastfmSource *source = RB_LASTFM_SOURCE (user_data);
829 char *body;
830 char **pieces;
831 int i;
833 if ((req->response).body == NULL) {
834 rb_debug ("Lastfm: Server failed to respond");
835 return;
838 body = g_malloc0 ((req->response).length + 1);
839 memcpy (body, (req->response).body, (req->response).length);
841 rb_debug ("response body: %s", body);
843 if (strstr (body, "ERROR - no such artist") != NULL) {
844 source->priv->status = NO_ARTIST;
847 g_strstrip (body);
848 pieces = g_strsplit (body, "\n", 6);
849 for (i = 0; pieces[i] != NULL; i++) {
850 gchar **values = g_strsplit (pieces[i], "=", 2);
851 if (strcmp (values[0], "session") == 0) {
852 if (strcmp (values[1], "FAILED") == 0) {
853 source->priv->status = FAILED;
854 rb_debug ("Lastfm failed to connect to the server");
855 break;
857 source->priv->status = OK;
858 source->priv->session = g_strdup (values[1]);
859 rb_debug ("session ID: %s", source->priv->session);
860 source->priv->connected = TRUE;
861 } else if (strcmp (values[0], "stream_url") == 0) {
862 source->priv->stream_url = g_strdup (values[1]);
863 rb_debug ("stream url: %s", source->priv->stream_url);
864 } else if (strcmp (values[0], "subscriber") == 0) {
865 if (strcmp (values[1], "0") == 0) {
866 source->priv->subscriber = FALSE;
867 } else {
868 source->priv->subscriber = TRUE;
870 } else if (strcmp (values[0], "framehack") ==0 ) {
871 if (strcmp (values[1], "0") == 0) {
872 source->priv->framehack = FALSE;
873 } else {
874 source->priv->framehack = TRUE;
876 } else if (strcmp (values[0], "base_url") ==0) {
877 source->priv->base_url = g_strdup (values[1]);
878 } else if (strcmp (values[0], "base_path") ==0) {
879 source->priv->base_path = g_strdup (values[1]);
880 } else if (strcmp (values[0], "update_url") ==0) {
881 source->priv->update_url = g_strdup (values[1]);
882 } else if (strcmp (values[0], "banned") ==0) {
883 if (strcmp (values[1], "0") ==0) {
884 source->priv->banned = FALSE;
885 } else {
886 source->priv->status = BANNED;
887 source->priv->banned = TRUE;
888 source->priv->connected = FALSE;
890 } else if (strcmp (values[0], "response") == 0) {
891 if (strcmp (values[1], "OK") == 0) {
892 source->priv->status = OK;
893 rb_debug ("Successfully communicated");
894 source->priv->connected = TRUE;
895 } else {
896 source->priv->connected = FALSE;
898 } else if (strcmp (values[0], "stationname") == 0) {
899 gchar **data = g_strsplit (g_strdown(pieces[i - 1]), "=",2);
900 RhythmDBEntry *entry;
901 GValue titlestring = {0,};
903 rb_debug ("Received station name from server: %s", values[1]);
904 entry = rhythmdb_entry_lookup_by_location (source->priv->db, data[1]);
905 g_value_init (&titlestring, G_TYPE_STRING);
906 g_value_set_string (&titlestring, values[1]);
908 if (entry == NULL) {
909 entry = rhythmdb_entry_new (source->priv->db, source->priv->entry_type, data[1]);
910 rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_GENRE, &titlestring);
911 } else {
912 rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_GENRE, &titlestring);
914 g_value_unset (&titlestring);
915 rhythmdb_commit (source->priv->db);
920 g_strfreev (pieces);
921 g_free (body);
923 /* doesn't work yet
924 if (source->priv->pending_entry) {
925 rb_shell_player_play_entry (source->priv->shell_player,
926 source->priv->pending_entry,
927 NULL);
928 rhythmdb_entry_unref (source->priv->pending_entry);
929 source->priv->pending_entry = NULL;
933 rb_source_notify_status_changed (RB_SOURCE (source));
934 g_object_unref (source);
937 static void
938 rb_lastfm_change_station (RBLastfmSource *source, const char *station)
940 char *url;
941 if (!source->priv->connected) {
942 rb_lastfm_source_do_handshake (source);
943 return;
946 url = g_strdup_printf("%s/radio/adjust.php?session=%s&url=%s&debug=0",
947 LASTFM_URL,
948 source->priv->session,
949 station);
951 g_object_ref (source);
952 rb_lastfm_perform (source, url, NULL, rb_lastfm_message_cb);
953 g_free (url);
956 static void
957 rb_lastfm_proxy_config_changed_cb (RBProxyConfig *config,
958 RBLastfmSource *source)
960 SoupUri *uri;
962 if (source->priv->soup_session) {
963 uri = rb_proxy_config_get_libsoup_uri (config);
964 g_object_set (G_OBJECT (source->priv->soup_session),
965 "proxy-uri", uri,
966 NULL);
967 if (uri)
968 soup_uri_free (uri);
972 static void
973 rb_lastfm_source_new_station (char *uri, char *title, RBLastfmSource *source)
975 RhythmDBEntry *entry;
976 GValue v = {0,};
978 rb_debug ("adding lastfm: %s, %s",uri, title);
980 entry = rhythmdb_entry_lookup_by_location (source->priv->db, uri);
981 if (entry) {
982 rb_debug ("uri %s already in db", uri);
983 return;
986 entry = rhythmdb_entry_new (source->priv->db, source->priv->entry_type, uri);
987 g_value_init (&v, G_TYPE_STRING);
988 g_value_set_string (&v, title);
989 rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_GENRE, &v);
990 g_value_unset (&v);
992 g_value_init (&v, G_TYPE_DOUBLE);
993 g_value_set_double (&v, 0.0);
994 rhythmdb_entry_set (source->priv->db, entry, RHYTHMDB_PROP_RATING, &v);
996 rhythmdb_commit (source->priv->db);
999 static void
1000 rb_lastfm_source_dispose (GObject *object)
1002 RBLastfmSource *source;
1004 source = RB_LASTFM_SOURCE (object);
1006 if (source->priv->db) {
1007 g_object_unref (source->priv->db);
1008 source->priv->db = NULL;
1011 G_OBJECT_CLASS (rb_lastfm_source_parent_class)->dispose (object);
1014 static void
1015 rb_lastfm_source_command (RBLastfmSource *source, const char *query_string, const char *status)
1017 char *url;
1018 if (!source->priv->connected) {
1019 rb_lastfm_source_do_handshake (source);
1020 return;
1023 url = g_strdup_printf ("%s/radio/control.php?session=%s&debug=0&%s",
1024 LASTFM_URL,
1025 source->priv->session,
1026 query_string);
1027 g_object_ref (source);
1028 rb_lastfm_perform (source, url, NULL, rb_lastfm_message_cb);
1029 g_free (url);
1031 rb_source_notify_status_changed (RB_SOURCE (source));
1034 static void
1035 rb_lastfm_source_love_track (GtkAction *action, RBLastfmSource *source)
1037 rb_lastfm_source_command (source, "command=love", _("Marking song loved..."));
1040 static void
1041 rb_lastfm_source_skip_track (GtkAction *action, RBLastfmSource *source)
1043 rb_lastfm_source_command (source, "command=skip", _("Skipping song..."));
1046 static void
1047 rb_lastfm_source_ban_track (GtkAction *action, RBLastfmSource *source)
1049 rb_lastfm_source_command (source, "command=ban", _("Banning song..."));
1053 static void
1054 rb_lastfm_source_drag_cb (GtkWidget *widget,
1055 GdkDragContext *dc,
1056 gint x, gint y,
1057 GtkSelectionData *selection_data,
1058 guint info, guint time,
1059 RBLastfmSource *source)
1061 impl_receive_drag (RB_SOURCE (source) , selection_data);
1064 static gboolean
1065 impl_receive_drag (RBSource *asource, GtkSelectionData *selection_data)
1067 char *uri;
1068 char *title = NULL;
1069 RBLastfmSource *source = RB_LASTFM_SOURCE (asource);
1071 uri = (char *)selection_data->data;
1072 rb_debug ("parsing uri %s", uri);
1074 if (strstr (uri, "lastfm://") == NULL)
1075 return FALSE;
1077 title = rb_lastfm_source_title_from_uri (uri);
1079 rb_lastfm_source_new_station (uri, title, source);
1080 return TRUE;
1084 static char *
1085 rb_lastfm_source_title_from_uri (char *uri)
1087 char *title = NULL;
1088 char *unesc_title;
1089 gchar **data = g_strsplit (uri, "/", 0);
1091 if (strstr (uri, "globaltags") != NULL)
1092 title = g_strdup_printf (_("Global Tag %s"), data[3]);
1094 if (title == NULL && strcmp (data[2], "artist") == 0) {
1095 /* Check if the station is from an artist page, if not, it is a similar
1096 * artist station, and the server should return a name that change_station
1097 * will handle for us.
1099 if (data[4] != NULL) {
1100 if (strcmp (data[4], "similarartists") == 0)
1101 title = g_strdup_printf (_("Artists similar to %s"), data[3]);
1102 if (strcmp (data[4], "fans") == 0)
1103 title = g_strdup_printf (_("Artists Liked by fans of %s"), data[3]);
1108 if (title == NULL && strcmp (data[2], "user") == 0) {
1109 if (strcmp(data[4], "neighbours") == 0)
1110 title = g_strdup_printf (_("%s's Neighbour Radio"), data[3]);
1111 if (strcmp(data[4], "recommended") == 0)
1112 title = g_strdup_printf (_("%s's Recommended Radio: %s percent"), data[3], data[5]);
1113 /* subscriber? */
1116 if (title == NULL) {
1117 title = g_strstrip (uri);
1120 g_strfreev (data);
1121 unesc_title = gnome_vfs_unescape_string (title, NULL);
1122 g_free (title);
1123 return unesc_title;
1126 static void
1127 rb_lastfm_source_add_station_cb (GtkButton *button, gpointer *data)
1129 RBLastfmSource *source = RB_LASTFM_SOURCE (data);
1130 const gchar *add;
1131 char *title;
1132 char *uri;
1134 add = gtk_entry_get_text (GTK_ENTRY (source->priv->txtbox));
1136 if (strcmp (gtk_combo_box_get_active_text (GTK_COMBO_BOX (source->priv->typecombo)), "Artist") == 0) {
1137 uri = g_strdup_printf ("lastfm://artist/%s/similarartists", add);
1138 } else {
1139 uri = g_strdup_printf ("lastfm://globaltags/%s", add);
1142 gtk_entry_set_text (GTK_ENTRY (source->priv->txtbox), "");
1143 title = rb_lastfm_source_title_from_uri (uri);
1144 rb_lastfm_source_new_station (uri, title, source);
1146 g_free(uri);
1147 g_free(title);
1150 static void
1151 rb_lastfm_source_metadata_cb (SoupMessage *req, RBLastfmSource *source)
1153 char *body;
1154 char **pieces;
1155 int p;
1156 RhythmDBEntry *entry;
1158 entry = rb_shell_player_get_playing_entry (source->priv->shell_player);
1159 if (entry == NULL || rhythmdb_entry_get_entry_type (entry) != source->priv->entry_type) {
1160 rb_debug ("got response to metadata request, but not playing from this source");
1161 return;
1164 rb_debug ("got response to metadata request");
1165 body = g_malloc0 ((req->response).length + 1);
1166 memcpy (body, (req->response).body, (req->response).length);
1168 g_strstrip (body);
1169 pieces = g_strsplit (body, "\n", 0);
1171 for (p = 0; pieces[p] != NULL; p++) {
1172 gchar **values;
1173 GValue v = {0,};
1175 values = g_strsplit (pieces[p], "=", 2);
1176 if (strcmp (values[0], "station") == 0) {
1177 } else if (strcmp (values[0], "station_url") == 0) {
1178 } else if (strcmp (values[0], "stationfeed") == 0) {
1179 } else if (strcmp (values[0], "stationfeed_url") == 0) {
1180 } else if (strcmp (values[0], "artist") == 0) {
1181 rb_debug ("artist -> %s", values[1]);
1182 g_free (source->priv->streaming_artist);
1183 source->priv->streaming_artist = g_strdup (values[1]);
1185 g_value_init (&v, G_TYPE_STRING);
1186 g_value_set_string (&v, values[1]);
1187 rhythmdb_emit_entry_extra_metadata_notify (source->priv->db,
1188 entry,
1189 RHYTHMDB_PROP_STREAM_SONG_ARTIST,
1190 &v);
1191 g_value_unset (&v);
1192 } else if (strcmp (values[0], "album") == 0) {
1193 rb_debug ("album -> %s", values[1]);
1194 g_free (source->priv->streaming_album);
1195 source->priv->streaming_album = g_strdup (values[1]);
1197 g_value_init (&v, G_TYPE_STRING);
1198 g_value_set_string (&v, values[1]);
1199 rhythmdb_emit_entry_extra_metadata_notify (source->priv->db,
1200 entry,
1201 RHYTHMDB_PROP_STREAM_SONG_ALBUM,
1202 &v);
1203 g_value_unset (&v);
1204 } else if (strcmp (values[0], "track") == 0) {
1205 rb_debug ("track -> %s", values[1]);
1207 g_free (source->priv->streaming_title);
1208 source->priv->streaming_title = g_strdup (values[1]);
1210 g_value_init (&v, G_TYPE_STRING);
1211 g_value_set_string (&v, values[1]);
1212 rhythmdb_emit_entry_extra_metadata_notify (source->priv->db,
1213 entry,
1214 RHYTHMDB_PROP_STREAM_SONG_TITLE,
1215 &v);
1216 g_value_unset (&v);
1217 } else if (strcmp (values[0], "albumcover_small") == 0) {
1218 } else if (strcmp (values[0], "albumcover_medium") == 0) {
1219 } else if (strcmp (values[0], "albumcover_large") == 0) {
1220 } else if (strcmp (values[0], "trackprogress") == 0) {
1221 } else if (strcmp (values[0], "trackduration") == 0) {
1222 } else if (strcmp (values[0], "artist_url") == 0) {
1223 } else if (strcmp (values[0], "album_url") == 0) {
1224 } else if (strcmp (values[0], "track_url") == 0) {
1225 } else if (strcmp (values[0], "discovery") == 0) {
1226 } else {
1227 rb_debug ("got unknown value: %s", values[0]);
1230 g_strfreev (values);
1233 g_strfreev (pieces);
1234 g_free (body);
1236 source->priv->status = OK;
1237 rb_source_notify_status_changed (RB_SOURCE (source));
1240 static void
1241 rb_lastfm_source_new_song_cb (GObject *player_backend,
1242 gpointer data,
1243 RBLastfmSource *source)
1245 char *uri;
1246 rb_debug ("got new song");
1248 uri = g_strdup_printf ("http://%s%s/np.php?session=%s&debug=0",
1249 source->priv->base_url,
1250 source->priv->base_path,
1251 source->priv->session);
1252 rb_lastfm_perform (source, uri, NULL, (SoupMessageCallbackFn) rb_lastfm_source_metadata_cb);
1253 g_free (uri);
1256 static gboolean
1257 check_entry_type (RBLastfmSource *source, RhythmDBEntry *entry)
1259 RhythmDBEntryType entry_type;
1260 gboolean matches = FALSE;
1262 g_object_get (source, "entry-type", &entry_type, NULL);
1263 if (entry != NULL && rhythmdb_entry_get_entry_type (entry) == entry_type)
1264 matches = TRUE;
1265 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
1267 return matches;
1270 static void
1271 rb_lastfm_song_changed_cb (RBShellPlayer *player,
1272 RhythmDBEntry *entry,
1273 RBLastfmSource *source)
1275 const char *location;
1277 g_free (source->priv->streaming_title);
1278 g_free (source->priv->streaming_album);
1279 g_free (source->priv->streaming_artist);
1280 source->priv->streaming_title = NULL;
1281 source->priv->streaming_album = NULL;
1282 source->priv->streaming_artist = NULL;
1284 if (check_entry_type (source, entry)) {
1285 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
1286 /* this bit doesn't work */
1288 if (!source->priv->connected) {
1289 rb_lastfm_source_do_handshake (source);
1290 source->priv->pending_entry = rhythmdb_entry_ref (entry);
1291 rb_debug ("will play station %s once connected", location);
1292 } else {
1293 rb_debug ("switching to station %s", location);
1294 rb_lastfm_change_station (source, location);
1297 rb_lastfm_change_station (source, location);
1298 } else {
1299 rb_debug ("non-lastfm entry being played");
1303 static GValue *
1304 streaming_title_request_cb (RhythmDB *db,
1305 RhythmDBEntry *entry,
1306 RBLastfmSource *source)
1308 GValue *value;
1309 if (check_entry_type (source, entry) == FALSE ||
1310 entry != rb_shell_player_get_playing_entry (source->priv->shell_player) ||
1311 source->priv->streaming_title == NULL)
1312 return NULL;
1314 rb_debug ("returning streaming title \"%s\" to extra metadata request", source->priv->streaming_title);
1315 value = g_new0 (GValue, 1);
1316 g_value_init (value, G_TYPE_STRING);
1317 g_value_set_string (value, source->priv->streaming_title);
1318 return value;
1321 static GValue *
1322 streaming_artist_request_cb (RhythmDB *db,
1323 RhythmDBEntry *entry,
1324 RBLastfmSource *source)
1326 GValue *value;
1328 if (check_entry_type (source, entry) == FALSE ||
1329 entry != rb_shell_player_get_playing_entry (source->priv->shell_player) ||
1330 source->priv->streaming_artist == NULL)
1331 return NULL;
1333 rb_debug ("returning streaming artist \"%s\" to extra metadata request", source->priv->streaming_artist);
1334 value = g_new0 (GValue, 1);
1335 g_value_init (value, G_TYPE_STRING);
1336 g_value_set_string (value, source->priv->streaming_artist);
1337 return value;
1340 static GValue *
1341 streaming_album_request_cb (RhythmDB *db,
1342 RhythmDBEntry *entry,
1343 RBLastfmSource *source)
1345 GValue *value;
1347 if (check_entry_type (source, entry) == FALSE ||
1348 entry != rb_shell_player_get_playing_entry (source->priv->shell_player) ||
1349 source->priv->streaming_artist == NULL)
1350 return NULL;
1352 rb_debug ("returning streaming album \"%s\" to extra metadata request", source->priv->streaming_album);
1353 value = g_new0 (GValue, 1);
1354 g_value_init (value, G_TYPE_STRING);
1355 g_value_set_string (value, source->priv->streaming_album);
1356 return value;
1359 static void
1360 extra_metadata_gather_cb (RhythmDB *db,
1361 RhythmDBEntry *entry,
1362 GHashTable *data,
1363 RBLastfmSource *source)
1365 /* our extra metadata only applies to the playing entry */
1366 if (entry != rb_shell_player_get_playing_entry (source->priv->shell_player) ||
1367 check_entry_type (source, entry) == FALSE)
1368 return;
1370 if (source->priv->streaming_title != NULL) {
1371 GValue *value;
1373 value = g_new0 (GValue, 1);
1374 g_value_init (value, G_TYPE_STRING);
1375 g_value_set_string (value, source->priv->streaming_title);
1376 g_hash_table_insert (data, g_strdup (RHYTHMDB_PROP_STREAM_SONG_TITLE), value);
1379 if (source->priv->streaming_artist != NULL) {
1380 GValue *value;
1382 value = g_new0 (GValue, 1);
1383 g_value_init (value, G_TYPE_STRING);
1384 g_value_set_string (value, source->priv->streaming_artist);
1385 g_hash_table_insert (data, g_strdup (RHYTHMDB_PROP_STREAM_SONG_ARTIST), value);
1388 if (source->priv->streaming_album != NULL) {
1389 GValue *value;
1391 value = g_new0 (GValue, 1);
1392 g_value_init (value, G_TYPE_STRING);
1393 g_value_set_string (value, source->priv->streaming_album);
1394 g_hash_table_insert (data, g_strdup (RHYTHMDB_PROP_STREAM_SONG_ALBUM), value);
1398 static void
1399 impl_activate (RBSource *source)
1401 rb_lastfm_source_do_handshake (RB_LASTFM_SOURCE (source));
1404 static void
1405 buffering_cb (GObject *backend, guint progress, RBLastfmSource *source)
1407 if (progress == 0)
1408 return;
1410 if (progress == 100)
1411 progress = 0;
1413 rb_debug ("buffer at %d%%", progress);
1415 GDK_THREADS_ENTER ();
1416 source->priv->buffering = progress;
1417 rb_source_notify_status_changed (RB_SOURCE (source));
1418 GDK_THREADS_LEAVE ();
1421 static void
1422 playing_source_changed_cb (RBShellPlayer *player,
1423 RBSource *source,
1424 RBLastfmSource *lastfm_source)
1426 GObject *backend;
1428 g_object_get (player, "player", &backend, NULL);
1430 if (source == RB_SOURCE (lastfm_source)) {
1431 rb_debug ("connecting buffering signal handler");
1432 if (lastfm_source->priv->buffering_id == 0) {
1433 lastfm_source->priv->buffering_id =
1434 g_signal_connect_object (backend, "buffering",
1435 G_CALLBACK (buffering_cb),
1436 lastfm_source, 0);
1438 /* display 'connecting' status until we get a buffering message */
1439 lastfm_source->priv->buffering = -1;
1440 rb_source_notify_status_changed (RB_SOURCE (lastfm_source));
1441 } else if (lastfm_source->priv->buffering_id > 0) {
1442 rb_debug ("disconnecting buffering signal handler");
1443 g_signal_handler_disconnect (backend, lastfm_source->priv->buffering_id);
1444 lastfm_source->priv->buffering_id = 0;
1446 lastfm_source->priv->buffering = -1;
1447 rb_source_notify_status_changed (RB_SOURCE (lastfm_source));
1450 g_object_unref (backend);