diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am
index c30a93e..8120c5b 100644
--- a/bindings/python/Makefile.am
+++ b/bindings/python/Makefile.am
@@ -70,7 +70,7 @@ BINDING_HEADERS_SRCDIR_IN = \
 	sources/rb-sourcelist.h			\
 	sources/rb-sourcelist-model.h		\
 	sources/rb-static-playlist-source.h	\
-	sources/rb-streaming-source.h		\
+	sources/rb-streaming-signal-handler.h	\
 	widgets/rb-entry-view.h			\
 	widgets/rb-library-browser.h		\
 	widgets/rb-property-view.h		\
diff --git a/bindings/python/rb.defs b/bindings/python/rb.defs
index f09c855..70ac5c8 100644
--- a/bindings/python/rb.defs
+++ b/bindings/python/rb.defs
@@ -64,11 +64,11 @@
   (gtype-id "RB_TYPE_AUTO_PLAYLIST_SOURCE")
 )
 
-(define-object StreamingSource
+(define-object StreamingSignalHandler
   (in-module "RB")
-  (parent "RBSource")
-  (c-name "RBStreamingSource")
-  (gtype-id "RB_TYPE_STREAMING_SOURCE")
+  (parent "GObject")
+  (c-name "RBStreamingSignalHandler")
+  (gtype-id "RB_TYPE_STREAMING_SIGNAL_HANDLER")
 )
 
 (define-object RemovableMediaManager
@@ -1852,51 +1852,65 @@
   )
 )
 
-;; From rb-streaming-source.h
 
-(define-function rb_streaming_source_get_type
-  (c-name "rb_streaming_source_get_type")
+;; From rb-streaming-signal-handler.h
+
+(define-function rb_streaming_signal_handler_get_type
+  (c-name "rb_streaming_signal_handler_get_type")
   (return-type "GType")
 )
 
-(define-method get_progress
-  (of-object "RBStreamingSource")
-  (c-name "rb_streaming_source_get_progress")
+(define-function rb_streaming_signal_handler_new
+  (c-name "rb_streaming_signal_handler_new")
+  (is-constructor-of "RbStreamingSignalHandler")
+  (return-type "RBStreamingSignalHandler*")
+  (properties
+    '("source")
+    '("only_streams")
+    '("handle_metadata_requests")
+    '("handle_metadata_collection")
+    '("handle_metadata_db_updates")
+    '("handle_progress_maintenance")
+  )
+)
+
+(define-method set_title
+  (of-object "RBStreamingSignalHandler")
+  (c-name "rb_streaming_signal_handler_set_title")
   (return-type "none")
   (parameters
-    '("char**" "progress_text")
-    '("float*" "progress")
+    '("gchar*" "title")
   )
 )
 
-(define-method set_streaming_title
-  (of-object "RBStreamingSource")
-  (c-name "rb_streaming_source_set_streaming_title")
+(define-method set_artist
+  (of-object "RBStreamingSignalHandler")
+  (c-name "rb_streaming_signal_handler_set_artist")
   (return-type "none")
   (parameters
-    '("const-char*" "title")
+    '("gchar*" "artist")
   )
 )
 
-(define-method set_streaming_artist
-  (of-object "RBStreamingSource")
-  (c-name "rb_streaming_source_set_streaming_artist")
+(define-method set_album
+  (of-object "RBStreamingSignalHandler")
+  (c-name "rb_streaming_signal_handler_set_album")
   (return-type "none")
   (parameters
-    '("const-char*" "artist")
+    '("gchar*" "album")
   )
 )
 
-(define-method set_streaming_album
-  (of-object "RBStreamingSource")
-  (c-name "rb_streaming_source_set_streaming_album")
+(define-method get_progress
+  (of-object "RBStreamingSignalHandler")
+  (c-name "rb_streaming_signal_handler_get_progress")
   (return-type "none")
   (parameters
-    '("const-char*" "album")
+    '("gchar**" "progress_text")
+    '("gfloat*" "progress_value")
   )
 )
 
-
 ;; From ../../widgets/rb-song-info.h
 
 (define-function rb_song_info_get_type
diff --git a/bindings/python/rb.override b/bindings/python/rb.override
index 065c0a1..c3feba2 100644
--- a/bindings/python/rb.override
+++ b/bindings/python/rb.override
@@ -32,7 +32,7 @@ headers
 #include "rb-sourcelist.h"
 #include "rb-sourcelist-model.h"
 #include "rb-static-playlist-source.h"
-#include "rb-streaming-source.h"
+#include "rb-streaming-signal-handler.h"
 #include "rb-uri-dialog.h"
 
 void pyrb_register_classes (PyObject *d);
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 6cd69c2..0d5ceec 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -47,6 +47,11 @@ INCLUDES =						\
 	-DSHARE_DIR=\"$(pkgdatadir)\"			\
 	-DDATADIR=\""$(datadir)"\"			\
 	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/sources				\
 	-I$(top_srcdir)/widgets				\
 	$(SOUP_CFLAGS)					\
 	$(RHYTHMBOX_CFLAGS)
diff --git a/plugins/audioscrobbler/rb-lastfm-source.c b/plugins/audioscrobbler/rb-lastfm-source.c
index 9661fcd..57cb9a3 100644
--- a/plugins/audioscrobbler/rb-lastfm-source.c
+++ b/plugins/audioscrobbler/rb-lastfm-source.c
@@ -54,6 +54,7 @@
 
 #include "rb-audioscrobbler.h"
 #include "rb-lastfm-source.h"
+#include "rb-streaming-signal-handler.h"
 
 #include "rhythmdb-query-model.h"
 #include "rb-glade-helpers.h"
@@ -148,6 +149,7 @@ static void rb_lastfm_song_changed_cb (RBShellPlayer *player, RhythmDBEntry *ent
 static void show_entry_popup (RBEntryView *view,
 			      gboolean over_entry,
 			      RBSource *source);
+static RBSourceEOFType impl_handle_eos (RBSource *source);
 
 /* can't be bothered creating a whole header file just for this: */
 GType rb_lastfm_src_get_type (void);
@@ -202,9 +204,11 @@ struct RBLastfmSourcePrivate
 
 	SoupSession *soup_session;
 	RBProxyConfig *proxy_config;
+
+	RBStreamingSignalHandler *streaming_signal_handler;
 };
 
-G_DEFINE_TYPE (RBLastfmSource, rb_lastfm_source, RB_TYPE_STREAMING_SOURCE);
+G_DEFINE_TYPE (RBLastfmSource, rb_lastfm_source, RB_TYPE_SOURCE);
 #define RB_LASTFM_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LASTFM_SOURCE, RBLastfmSourcePrivate))
 
 enum
@@ -248,6 +252,9 @@ rb_lastfm_source_class_init (RBLastfmSourceClass *klass)
 	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_try_playlist = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_handle_eos = impl_handle_eos;
 	source_class->impl_delete = impl_delete;
 	source_class->impl_get_entry_view = impl_get_entry_view;
 	source_class->impl_get_status = impl_get_status;
@@ -426,6 +433,14 @@ rb_lastfm_source_constructor (GType type, guint n_construct_properties,
 				 source,
 				 0);
 
+	source->priv->streaming_signal_handler =
+		rb_streaming_signal_handler_new (RB_SOURCE (source),
+						 TRUE,
+						 TRUE,
+						 FALSE,
+						 FALSE,
+						 TRUE);
+
 	return G_OBJECT (source);
 }
 
@@ -606,7 +621,11 @@ impl_get_status (RBSource *asource, char **text, char **progress_text, float *pr
 			break;
 		}
 	}
-	rb_streaming_source_get_progress (RB_STREAMING_SOURCE (source), progress_text, progress);
+
+	rb_streaming_signal_handler_get_progress (
+		source->priv->streaming_signal_handler,
+		progress_text,
+		progress);
 }
 
 static void
@@ -974,6 +993,8 @@ rb_lastfm_source_dispose (GObject *object)
 		source->priv->db = NULL;
 	}
 
+	g_object_unref (source->priv->streaming_signal_handler);
+
 	G_OBJECT_CLASS (rb_lastfm_source_parent_class)->dispose (object);
 }
 
@@ -1235,13 +1256,19 @@ rb_lastfm_source_metadata_cb (SoupMessage *req, RBLastfmSource *source)
 		} else if (strcmp (values[0], "stationfeed_url") == 0) {
 		} else if (strcmp (values[0], "artist") == 0) {
 			rb_debug ("artist -> %s", values[1]);
-			rb_streaming_source_set_streaming_artist (RB_STREAMING_SOURCE (source), values[1]);
+			rb_streaming_signal_handler_set_artist (
+				source->priv->streaming_signal_handler,
+				values[1]);
 		} else if (strcmp (values[0], "album") == 0) {
 			rb_debug ("album -> %s", values[1]);
-			rb_streaming_source_set_streaming_album (RB_STREAMING_SOURCE (source), values[1]);
+			rb_streaming_signal_handler_set_album (
+				source->priv->streaming_signal_handler,
+				values[1]);
 		} else if (strcmp (values[0], "track") == 0) {
 			rb_debug ("track -> %s", values[1]);
-			rb_streaming_source_set_streaming_title (RB_STREAMING_SOURCE (source), values[1]);
+			rb_streaming_signal_handler_set_title (
+				source->priv->streaming_signal_handler,
+				values[1]);
 		} else if (strcmp (values[0], "albumcover_small") == 0) {
 		} else if (strcmp (values[0], "albumcover_medium") == 0) {
 			GValue v = {0,};
@@ -1410,3 +1437,8 @@ impl_add_uri (RBSource *source, const char *uri, const char *title, const char *
 	return TRUE;
 }
 
+static RBSourceEOFType
+impl_handle_eos (RBSource *source)
+{
+	return RB_SOURCE_EOF_RETRY;
+}
diff --git a/plugins/audioscrobbler/rb-lastfm-source.h b/plugins/audioscrobbler/rb-lastfm-source.h
index dcd2e7b..da5995c 100644
--- a/plugins/audioscrobbler/rb-lastfm-source.h
+++ b/plugins/audioscrobbler/rb-lastfm-source.h
@@ -23,7 +23,7 @@
 #define __RB_LASTFM_SOURCE_H
 
 #include "rb-shell.h"
-#include "rb-streaming-source.h"
+#include "rb-source.h"
 
 G_BEGIN_DECLS
 
@@ -38,14 +38,14 @@ typedef struct RBLastfmSourcePrivate RBLastfmSourcePrivate;
 
 typedef struct
 {
-	RBStreamingSource parent;
+	RBSource parent;
 
 	RBLastfmSourcePrivate *priv;
 } RBLastfmSource;
 
 typedef struct
 {
-	RBStreamingSourceClass parent;
+	RBSourceClass parent;
 } RBLastfmSourceClass;
 
 GType		rb_lastfm_source_get_type	(void);
diff --git a/plugins/daap/Makefile.am b/plugins/daap/Makefile.am
index 84a9ea7..56db29f 100644
--- a/plugins/daap/Makefile.am
+++ b/plugins/daap/Makefile.am
@@ -41,6 +41,7 @@ endif
 
 libdaap_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
 libdaap_la_LIBADD = 					\
+	$(top_builddir)/corelib/librhythmbox-core.la	\
 	$(SOUP_LIBS)					\
 	$(MDNS_LIBS)
 
diff --git a/plugins/daap/rb-daap-source.c b/plugins/daap/rb-daap-source.c
index 6366e30..5ee2417 100644
--- a/plugins/daap/rb-daap-source.c
+++ b/plugins/daap/rb-daap-source.c
@@ -43,6 +43,7 @@
 #include "rb-dialog.h"
 #include "rb-preferences.h"
 #include "rb-daap-src.h"
+#include "rb-streaming-signal-handler.h"
 
 #include "rb-daap-connection.h"
 #include "rb-daap-mdns-browser.h"
@@ -84,6 +85,7 @@ struct RBDAAPSourcePrivate
 	gpointer connection;
 
 	GSList *playlist_sources;
+	GSList *streaming_signal_handlers;
 
 	const char *connection_status;
 	float connection_progress;
@@ -540,6 +542,16 @@ rb_daap_source_connection_cb (RBDAAPConnection *connection,
 
 		rb_shell_append_source (shell, playlist_source, RB_SOURCE (daap_source));
 		daap_source->priv->playlist_sources = g_slist_prepend (daap_source->priv->playlist_sources, playlist_source);
+
+	/* Assign a streaming signal handler for this playlist source. */
+	daap_source->priv->streaming_signal_handlers = g_slist_append( 
+		daap_source->priv->streaming_signal_handlers,
+		rb_streaming_signal_handler_new (playlist_source,
+						 FALSE,
+						 TRUE,
+						 TRUE,
+						 FALSE,
+						 FALSE));
 	}
 
 	g_object_unref (shell);
@@ -594,6 +606,16 @@ rb_daap_source_activate (RBSource *source)
 				    (RBDAAPConnectionCallback) rb_daap_source_connection_cb,
 				    source);
 
+	/* Assign a streaming signal handler for the main source. */
+	daap_source->priv->streaming_signal_handlers = g_slist_append( 
+		daap_source->priv->streaming_signal_handlers,
+		rb_streaming_signal_handler_new (RB_SOURCE (daap_source),
+						 FALSE,
+						 TRUE,
+						 TRUE,
+						 FALSE,
+						 FALSE));
+
 	g_object_unref (G_OBJECT (db));
 	g_object_unref (G_OBJECT (shell));
 }
@@ -654,6 +676,13 @@ rb_daap_source_disconnect (RBDAAPSource *daap_source)
 	g_slist_free (daap_source->priv->playlist_sources);
 	daap_source->priv->playlist_sources = NULL;
 
+	for (l = daap_source->priv->streaming_signal_handlers; l != NULL; l = l->next) {
+		g_object_unref (l->data);
+	}
+
+	g_slist_free (daap_source->priv->streaming_signal_handlers);
+	daap_source->priv->streaming_signal_handlers = NULL;
+
 	/* we don't want these firing while we are disconnecting */
 	g_signal_handlers_disconnect_by_func (daap_source->priv->connection,
 					      G_CALLBACK (connection_connecting_cb),
diff --git a/plugins/iradio/rb-iradio-source.c b/plugins/iradio/rb-iradio-source.c
index 5889d56..623e43e 100644
--- a/plugins/iradio/rb-iradio-source.c
+++ b/plugins/iradio/rb-iradio-source.c
@@ -51,6 +51,7 @@
 #include "rb-metadata.h"
 #include "rb-plugin.h"
 #include "rb-cut-and-paste-code.h"
+#include "rb-streaming-signal-handler.h"
 
 /* icon names */
 #define IRADIO_SOURCE_ICON  "library-internet-radio"
@@ -116,10 +117,7 @@ static void stations_view_drag_data_received_cb (GtkWidget *widget,
 						 RBIRadioSource *source);
 static void rb_iradio_source_cmd_new_station (GtkAction *action,
 					      RBIRadioSource *source);
-
-static void playing_source_changed_cb (RBShellPlayer *player,
-				       RBSource *source,
-				       RBIRadioSource *iradio_source);
+static RBSourceEOFType impl_handle_eos (RBSource *source);
 
 #define CMD_PATH_SHOW_BROWSER "/commands/ShowBrowser"
 #define CMD_PATH_CURRENT_STATION "/commands/CurrentStation"
@@ -158,6 +156,8 @@ struct RBIRadioSourcePrivate
 	gint info_available_id;
 
 	gboolean dispose_has_run;
+
+	RBStreamingSignalHandler *streaming_signal_handler;
 };
 
 #define RB_IRADIO_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_IRADIO_SOURCE, RBIRadioSourcePrivate))
@@ -174,7 +174,7 @@ static const GtkTargetEntry stations_view_drag_types[] = {
 	{  "_NETSCAPE_URL", 0, 1 },
 };
 
-G_DEFINE_TYPE (RBIRadioSource, rb_iradio_source, RB_TYPE_STREAMING_SOURCE)
+G_DEFINE_TYPE (RBIRadioSource, rb_iradio_source, RB_TYPE_SOURCE)
 
 static void
 rb_iradio_source_class_init (RBIRadioSourceClass *klass)
@@ -193,6 +193,8 @@ rb_iradio_source_class_init (RBIRadioSourceClass *klass)
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_try_playlist = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_handle_eos = impl_handle_eos;
 	source_class->impl_delete = impl_delete;
 	source_class->impl_get_browser_key  = impl_get_browser_key;
 	source_class->impl_get_entry_view = impl_get_entry_view;
@@ -262,6 +264,8 @@ rb_iradio_source_dispose (GObject *object)
 	eel_gconf_notification_remove (source->priv->prefs_notify_id);
 	eel_gconf_notification_remove (source->priv->first_time_notify_id);
 
+	g_object_unref (source->priv->streaming_signal_handler);
+
 	G_OBJECT_CLASS (rb_iradio_source_parent_class)->dispose (object);
 }
 
@@ -365,9 +369,13 @@ rb_iradio_source_constructor (GType type,
 
 	rb_iradio_source_state_prefs_sync (source);
 
-	g_signal_connect_object (source->priv->player, "playing-source-changed",
-				 G_CALLBACK (playing_source_changed_cb),
-				 source, 0);
+	source->priv->streaming_signal_handler =
+		rb_streaming_signal_handler_new (RB_SOURCE (source),
+						 TRUE,
+						 TRUE,
+						 TRUE,
+						 TRUE,
+						 TRUE);
 
 	rb_iradio_source_do_query (source);
 
@@ -570,7 +578,10 @@ impl_get_status (RBSource *asource,
 	*text = g_strdup_printf (ngettext ("%d station", "%d stations", num_entries),
 				 num_entries);
 
-	rb_streaming_source_get_progress (RB_STREAMING_SOURCE (source), progress_text, progress);
+	rb_streaming_signal_handler_get_progress (
+		source->priv->streaming_signal_handler,
+		progress_text,
+		progress);
 }
 
 static char *
@@ -1038,167 +1049,8 @@ rb_iradio_source_cmd_new_station (GtkAction *action,
 	gtk_widget_destroy (dialog);
 }
 
-static gboolean
-check_entry_type (RBIRadioSource *source, RhythmDBEntry *entry)
-{
-	RhythmDBEntryType entry_type;
-	gboolean matches = FALSE;
-
-	g_object_get (source, "entry-type", &entry_type, NULL);
-	if (entry != NULL && rhythmdb_entry_get_entry_type (entry) == entry_type)
-		matches = TRUE;
-	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
-
-	return matches;
-}
-
-static void
-info_available_cb (RBPlayer *backend,
-		   const char *uri,
-		   RBMetaDataField field,
-		   GValue *value,
-		   RBIRadioSource *source)
-{
-	RhythmDBEntry *entry;
-        RhythmDBPropType entry_field = 0;
-        gboolean set_field = FALSE;
-	char *str = NULL;
-
-	/* sanity check */
-	if (!rb_player_opened (backend)) {
-		rb_debug ("Got info_available but not playing");
-		return;
-	}
-
-	GDK_THREADS_ENTER ();
-
-	entry = rb_shell_player_get_playing_entry (source->priv->player);
-	if (check_entry_type (source, entry) == FALSE)
-		goto out_unlock;
-
-	/* validate the value */
-	switch (field) {
-	case RB_METADATA_FIELD_TITLE:
-	case RB_METADATA_FIELD_ARTIST:
-	case RB_METADATA_FIELD_GENRE:
-	case RB_METADATA_FIELD_COMMENT:
-		str = g_value_dup_string (value);
-		if (!g_utf8_validate (str, -1, NULL)) {
-			g_warning ("Invalid UTF-8 from internet radio: %s", str);
-			g_free (str);
-			goto out_unlock;
-		}
-		break;
-	default:
-		break;
-	}
-
-
-	switch (field) {
-		/* streaming song information */
-	case RB_METADATA_FIELD_TITLE:
-	{
-		rb_streaming_source_set_streaming_title (RB_STREAMING_SOURCE (source), str);
-		break;
-	}
-	case RB_METADATA_FIELD_ARTIST:
-	{
-		rb_streaming_source_set_streaming_artist (RB_STREAMING_SOURCE (source), str);
-		break;
-	}
-
-		/* station information */
-	case RB_METADATA_FIELD_GENRE:
-	{
-		const char *existing;
-
-		/* check if the db entry already has a genre; if so, don't change it */
-		existing = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE);
-		if ((existing == NULL) ||
-		    (strcmp (existing, "") == 0) ||
-		    (strcmp (existing, _("Unknown")) == 0)) {
-			entry_field = RHYTHMDB_PROP_GENRE;
-			rb_debug ("setting genre of iradio station to %s", str);
-			set_field = TRUE;
-		} else {
-			rb_debug ("iradio station already has genre: %s; ignoring %s", existing, str);
-		}
-		break;
-	}
-	case RB_METADATA_FIELD_COMMENT:
-	{
-		const char *existing;
-		const char *location;
-
-		/* check if the db entry already has a title; if so, don't change it.
-		 * consider title==URI to be the same as no title, since that's what
-		 * happens for stations imported by DnD or commandline args.
-		 * if the station title really is the same as the URI, then surely
-		 * the station title in the stream metadata will say that too..
-		 */
-		existing = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
-		location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
-		if ((existing == NULL) ||
-		    (strcmp (existing, "") == 0) ||
-		    (strcmp (existing, location) == 0)) {
-			entry_field = RHYTHMDB_PROP_TITLE;
-			rb_debug ("setting title of iradio station to %s", str);
-			set_field = TRUE;
-		} else {
-			rb_debug ("iradio station already has title: %s; ignoring %s", existing, str);
-		}
-		break;
-	}
-	case RB_METADATA_FIELD_BITRATE:
-		if (!rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_BITRATE)) {
-			gulong bitrate;
-
-			/* GStreamer sends us bitrate in bps, but we need it in kbps*/
-			bitrate = g_value_get_ulong (value);
-			g_value_set_ulong (value, bitrate/1000);
-
-			rb_debug ("setting bitrate of iradio station to %lu",
-				  g_value_get_ulong (value));
-			entry_field = RHYTHMDB_PROP_BITRATE;
-			set_field = TRUE;
-		}
-		break;
-	default:
-		break;
-	}
-
-	if (set_field && entry_field != 0) {
-		rhythmdb_entry_set (source->priv->db, entry, entry_field, value);
-		rhythmdb_commit (source->priv->db);
-	}
-
-	g_free (str);
- out_unlock:
-	GDK_THREADS_LEAVE ();
-}
-
-static void
-playing_source_changed_cb (RBShellPlayer *player,
-			   RBSource *source,
-			   RBIRadioSource *iradio_source)
+static RBSourceEOFType
+impl_handle_eos (RBSource *source)
 {
-	GObject *backend;
-
-	g_object_get (player, "player", &backend, NULL);
-
-	if (source == RB_SOURCE (iradio_source) && (iradio_source->priv->info_available_id == 0)) {
-		rb_debug ("connecting info-available signal handler");
-		iradio_source->priv->info_available_id =
-			g_signal_connect_object (backend, "info",
-						 G_CALLBACK (info_available_cb),
-						 iradio_source, 0);
-	} else if (iradio_source->priv->info_available_id) {
-		rb_debug ("disconnecting info-available signal handler");
-		g_signal_handler_disconnect (backend,
-					     iradio_source->priv->info_available_id);
-		iradio_source->priv->info_available_id = 0;
-	}
-
-	g_object_unref (backend);
+	return RB_SOURCE_EOF_RETRY;
 }
-
diff --git a/plugins/iradio/rb-iradio-source.h b/plugins/iradio/rb-iradio-source.h
index 2aa5ce0..f5f8e7d 100644
--- a/plugins/iradio/rb-iradio-source.h
+++ b/plugins/iradio/rb-iradio-source.h
@@ -25,7 +25,6 @@
 
 #include "rb-shell.h"
 #include "rb-plugin.h"
-#include "rb-streaming-source.h"
 
 G_BEGIN_DECLS
 
@@ -40,14 +39,14 @@ typedef struct RBIRadioSourcePrivate RBIRadioSourcePrivate;
 
 typedef struct
 {
-	RBStreamingSource parent;
+	RBSource parent;
 
 	RBIRadioSourcePrivate *priv;
 } RBIRadioSource;
 
 typedef struct
 {
-	RBStreamingSourceClass parent;
+	RBSourceClass parent;
 } RBIRadioSourceClass;
 
 GType		rb_iradio_source_get_type	(void);
diff --git a/sources/Makefile.am b/sources/Makefile.am
index 0b3c4e7..070156f 100644
--- a/sources/Makefile.am
+++ b/sources/Makefile.am
@@ -5,10 +5,12 @@ noinst_LTLIBRARIES = libsources.la libsourcesimpl.la
 libsources_la_SOURCES = 		\
 	rb-source.c 			\
 	rb-source.h			\
-	rb-streaming-source.c		\
-	rb-streaming-source.h		\
 	rb-source-group.c		\
 	rb-source-group.h		\
+	rb-streaming-signal-handler.c	\
+	rb-streaming-signal-handler.h	\
+	rb-browser-source.c		\
+	rb-browser-source.h		\
 	$(NULL)
 
 libsourcesimpl_la_SOURCES =		\
@@ -16,8 +18,6 @@ libsourcesimpl_la_SOURCES =		\
 	rb-sourcelist.h			\
 	rb-sourcelist-model.c		\
 	rb-sourcelist-model.h		\
-	rb-browser-source.c		\
-	rb-browser-source.h		\
 	rb-library-source.c		\
 	rb-library-source.h		\
 	rb-podcast-source.c		\
@@ -49,8 +49,6 @@ INCLUDES =						\
 	-I$(top_srcdir)/metadata 			\
 	-I$(top_srcdir)/widgets 			\
 	-I$(top_srcdir)/library 			\
-	-I$(top_srcdir)/player	 			\
-	-I$(top_srcdir)/iradio				\
 	-I$(top_srcdir)/podcast				\
 	-I$(top_srcdir)/shell				\
 	-I$(top_srcdir)/backends			\
diff --git a/sources/rb-streaming-signal-handler.c b/sources/rb-streaming-signal-handler.c
new file mode 100644
index 0000000..c32b11d
--- /dev/null
+++ b/sources/rb-streaming-signal-handler.c
@@ -0,0 +1,753 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Provides signal handling functionality for streaming audio sources.
+ *  Maintains stream metadata and buffering information.
+ *
+ *  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 of the License, or
+ *  (at your option) any later 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 "rb-debug.h"
+#include "rb-player.h"
+#include "rb-shell.h"
+#include "rb-streaming-signal-handler.h"
+
+#include <glib/gi18n.h>
+#include <string.h>
+
+#define STREAMING_METADATA_NOTIFY_TIMEOUT 350
+
+/* Class members. */
+static void rb_streaming_signal_handler_dispose (GObject *object);
+
+/* Private signal and timeout handlers. */
+static GValue *streaming_title_request_cb  (RhythmDB *db,
+					    RhythmDBEntry *entry,
+					    RBStreamingSignalHandler *handler);
+static GValue *streaming_artist_request_cb (RhythmDB *db,
+					    RhythmDBEntry *entry,
+					    RBStreamingSignalHandler *handler);
+static GValue *streaming_album_request_cb  (RhythmDB *db,
+					    RhythmDBEntry *entry,
+					    RBStreamingSignalHandler *handler);
+static void extra_metadata_gather_cb       (RhythmDB *db,
+					    RhythmDBEntry *entry,
+					    RBStringValueMap *value_map,
+					    RBStreamingSignalHandler *handler);
+static void playing_source_changed_cb	   (RBShellPlayer *player,
+					    RBSource *source,
+					    RBStreamingSignalHandler *handler);
+static void info_available_cb		   (RBPlayer *backend,
+					    const gchar *uri,
+					    RBMetaDataField field,
+					    GValue *value,
+					    RBStreamingSignalHandler *handler);
+static gboolean emit_notification_cb	   (RBStreamingSignalHandler *handler);
+static void playing_song_changed_cb	   (RBShellPlayer *player,
+					    RhythmDBEntry *entry,
+					    RBStreamingSignalHandler *handler);
+static void buffering_cb		   (RBPlayer *player,
+					    gpointer unused,
+					    guint progress,
+					    RBStreamingSignalHandler *handler);
+
+G_DEFINE_TYPE (RBStreamingSignalHandler, rb_streaming_signal_handler, G_TYPE_OBJECT)
+
+struct RBStreamingSignalHandlerPrivate
+{
+	/* Maintainable and reportable metadata. */
+	gchar *title;
+	gchar *artist;
+	gchar *album;
+
+	/* Maintainable and queryable metadata. */
+	guint buffering;
+
+	/* Selected features. */
+	gboolean handle_metadata_requests;
+	gboolean handle_metadata_collection;
+	gboolean handle_metadata_db_updates;
+	gboolean handle_progress_maintenance;
+
+	/* Internal state. */
+	gboolean only_streams;
+	RBShellPlayer *shell_player;
+	RBSource *source;
+	RhythmDB *db;
+	RhythmDBEntry *playing_entry;
+
+	/* Signal and timeout handlers. */
+	gint streaming_title_request_id;
+	gint streaming_artist_request_id;
+	gint streaming_album_request_id;
+	gint extra_metadata_gather_id;
+	gint playing_source_changed_id;
+	gint info_available_id;
+	gint playing_song_changed_id;
+	gint buffering_id;
+	gint emit_notification_id;
+};
+
+static void
+rb_streaming_signal_handler_class_init (RBStreamingSignalHandlerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->dispose = rb_streaming_signal_handler_dispose;
+
+	g_type_class_add_private (klass, sizeof (RBStreamingSignalHandlerPrivate));
+}
+
+static void
+rb_streaming_signal_handler_init (RBStreamingSignalHandler *handler)
+{
+	handler->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		handler,
+		RB_TYPE_STREAMING_SIGNAL_HANDLER,
+		RBStreamingSignalHandlerPrivate);
+}
+
+/* Initialise a streaming signal handler. */ 
+RBStreamingSignalHandler *
+rb_streaming_signal_handler_new (RBSource *source,
+				 gboolean only_streams,
+				 gboolean handle_metadata_requests,
+				 gboolean handle_metadata_collection,
+				 gboolean handle_metadata_db_updates,
+				 gboolean handle_progress_maintenance)
+{
+	RBShell *shell;
+	RBStreamingSignalHandler *handler;
+
+	handler = g_object_new (RB_TYPE_STREAMING_SIGNAL_HANDLER, NULL);
+
+	/* Fill in persistent data. */
+	handler->priv->source = source;
+	handler->priv->only_streams = only_streams;
+	handler->priv->handle_metadata_requests = handle_metadata_requests;
+	handler->priv->handle_metadata_collection = handle_metadata_collection;
+	handler->priv->handle_metadata_db_updates = handle_metadata_db_updates;
+	handler->priv->handle_progress_maintenance = handle_progress_maintenance;
+
+	/* Connect required signal handlers. */
+	g_object_get (G_OBJECT (source), "shell", &shell, NULL);
+	g_object_get (G_OBJECT (shell),
+		"db", &handler->priv->db,
+		"shell-player", &handler->priv->shell_player,
+		NULL);
+	g_object_unref (shell);
+
+	if (handle_metadata_requests) {
+		handler->priv->streaming_title_request_id =
+			g_signal_connect_object (G_OBJECT (handler->priv->db),
+			"entry-extra-metadata-request::"
+			RHYTHMDB_PROP_STREAM_SONG_TITLE,
+			G_CALLBACK (streaming_title_request_cb),
+			handler, 0);
+		handler->priv->streaming_artist_request_id =
+			g_signal_connect_object (G_OBJECT (handler->priv->db), 
+			"entry-extra-metadata-request::"
+			RHYTHMDB_PROP_STREAM_SONG_ARTIST,
+			G_CALLBACK (streaming_artist_request_cb),
+			handler, 0);
+		handler->priv->streaming_album_request_id =
+			g_signal_connect_object (G_OBJECT (handler->priv->db),
+			"entry-extra-metadata-request::"
+			RHYTHMDB_PROP_STREAM_SONG_ALBUM,
+			G_CALLBACK (streaming_album_request_cb),
+			handler, 0);
+		handler->priv->extra_metadata_gather_id =
+			g_signal_connect_object (G_OBJECT (handler->priv->db),
+			"entry-extra-metadata-gather",
+			G_CALLBACK (extra_metadata_gather_cb),
+			handler, 0);
+	}
+
+	if (handle_metadata_collection || handle_metadata_db_updates) {
+		handler->priv->playing_source_changed_id =
+			g_signal_connect_object (G_OBJECT (handler->priv->shell_player),
+			"playing-source-changed",
+			G_CALLBACK (playing_source_changed_cb),
+			handler, 0);
+	}
+
+	handler->priv->playing_song_changed_id = g_signal_connect_object (
+		G_OBJECT (handler->priv->shell_player),
+		"playing-song-changed",
+		G_CALLBACK (playing_song_changed_cb),
+		handler, 0);
+
+	return handler;
+}
+
+static void
+rb_streaming_signal_handler_dispose (GObject *object)
+{
+	GObject *backend;
+	RBStreamingSignalHandler *handler;
+
+	handler = RB_STREAMING_SIGNAL_HANDLER (object);
+
+	/* Disconnect all signal handlers. */
+	g_object_get (handler->priv->shell_player, "player", &backend, NULL);
+
+	if (handler->priv->streaming_title_request_id) {
+		g_signal_handler_disconnect (handler->priv->db,
+			handler->priv->streaming_title_request_id);
+	}
+	if (handler->priv->streaming_artist_request_id) {
+		g_signal_handler_disconnect (handler->priv->db,
+			handler->priv->streaming_artist_request_id);
+	}
+	if (handler->priv->streaming_album_request_id) {
+		g_signal_handler_disconnect (handler->priv->db,
+			handler->priv->streaming_album_request_id);
+	}
+	if (handler->priv->extra_metadata_gather_id) {
+		g_signal_handler_disconnect (handler->priv->db,
+			handler->priv->extra_metadata_gather_id);
+	}
+	if (handler->priv->playing_source_changed_id) {
+		g_signal_handler_disconnect (handler->priv->shell_player,
+			handler->priv->playing_source_changed_id);
+	}
+	if (handler->priv->info_available_id) {
+		g_signal_handler_disconnect (backend,
+			handler->priv->info_available_id);
+	}
+	if (handler->priv->playing_song_changed_id) {
+		g_signal_handler_disconnect (handler->priv->shell_player,
+			handler->priv->playing_song_changed_id);
+	}
+	if (handler->priv->buffering_id) {
+		g_signal_handler_disconnect (backend,
+			handler->priv->buffering_id);
+	}
+	if (handler->priv->emit_notification_id) {
+		g_source_remove (handler->priv->emit_notification_id);
+	}
+
+	g_object_unref (backend);
+	g_object_unref (handler->priv->db);
+	g_object_unref (handler->priv->shell_player);
+
+	/* Free persistent data. */
+	g_free (handler->priv->title);
+	g_free (handler->priv->artist);
+	g_free (handler->priv->album);
+
+	if (handler->priv->playing_entry) {
+		rhythmdb_entry_unref (handler->priv->playing_entry);
+	}
+}
+
+/* Helper function for rb_streaming_signal_handler_set_* */
+static void
+set_streaming_metadata (RBStreamingSignalHandler *handler,
+	const gchar *metadata_field, gchar *metadata)
+{
+	GValue value = {0,};
+
+	/* Emit notification of the new metadata. */
+	g_value_init (&value, G_TYPE_STRING);
+	g_value_set_string (&value, metadata);
+	rhythmdb_emit_entry_extra_metadata_notify (handler->priv->db,
+						   handler->priv->playing_entry,
+						   metadata_field, &value);
+	g_value_unset (&value);
+
+	/* Add a timeout for the notification. */
+	if (handler->priv->emit_notification_id) {
+		g_source_remove (handler->priv->emit_notification_id);
+	}
+
+	handler->priv->emit_notification_id = g_timeout_add (
+		STREAMING_METADATA_NOTIFY_TIMEOUT,
+		(GSourceFunc)emit_notification_cb, handler);
+}
+
+/* Manually set the title be fed back in the metadata handler. */
+void
+rb_streaming_signal_handler_set_title (RBStreamingSignalHandler *handler,
+				       gchar *title)
+{
+	g_free (handler->priv->title);
+	handler->priv->title = g_strdup (title);
+
+	rb_debug ("Setting streaming audio title: \"%s\"",
+		  handler->priv->title);
+	set_streaming_metadata (handler, RHYTHMDB_PROP_STREAM_SONG_TITLE,
+				handler->priv->title);
+}
+
+/* Manually set the artist be fed back in the metadata handler. */
+void
+rb_streaming_signal_handler_set_artist (RBStreamingSignalHandler *handler,
+	gchar *artist)
+{
+	g_free (handler->priv->artist);
+	handler->priv->artist = g_strdup (artist);
+
+	rb_debug ("Setting streaming audio artist: \"%s\"",
+		handler->priv->artist);
+	set_streaming_metadata (handler, RHYTHMDB_PROP_STREAM_SONG_ARTIST,
+				handler->priv->artist);
+}
+
+/* Manually set the album be fed back in the metadata handler. */
+void
+rb_streaming_signal_handler_set_album (RBStreamingSignalHandler *handler,
+	gchar *album)
+{
+	g_free (handler->priv->album);
+	handler->priv->album = g_strdup (album);
+
+	rb_debug ("Setting streaming audio album: \"%s\"",
+		handler->priv->album);
+	set_streaming_metadata (handler, RHYTHMDB_PROP_STREAM_SONG_ALBUM,
+		handler->priv->album);
+}
+
+/* Inspect progress information collected by the progress handler. */
+void
+rb_streaming_signal_handler_get_progress (RBStreamingSignalHandler *handler,
+					  gchar **progress_text,
+					  gfloat *progress_value)
+{
+	*progress_value = 0.0f;
+	if (handler->priv->buffering == -1) {
+		*progress_text = g_strdup (_("Connecting"));
+	} else if (handler->priv->buffering > 0) {
+		*progress_value = ((gfloat)handler->priv->buffering) / 100.0f;
+		*progress_text = g_strdup (_("Buffering"));
+	}
+}
+
+static gboolean
+check_entry_type (RBStreamingSignalHandler *handler, RhythmDBEntry *entry)
+{
+	RhythmDBEntryType entry_type;
+	gboolean matches = FALSE;
+
+	g_object_get (handler->priv->source,
+		      "entry-type", &entry_type, NULL);
+	if (entry && rhythmdb_entry_get_entry_type (entry) == entry_type) {
+		matches = TRUE;
+	}
+	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
+
+	return matches;
+}
+
+/* Helper function for streaming_*_request_cb. */
+static GValue *
+metadata_request_helper (RBStreamingSignalHandler *handler,
+			 RhythmDBEntry *entry,
+			 gchar *metadata)
+{
+	GValue *value;
+
+	/* Bail out if validation checks fail or no metadata to report. */
+	if (!check_entry_type (handler, entry) ||
+	    !metadata ||
+	    entry != rb_shell_player_get_playing_entry (handler->priv->shell_player))
+	{
+		return NULL;
+	}
+
+	rb_debug ("Returning streaming metadata \"%s\" to extra metadata "
+		  "request", metadata);
+
+	/* Construct and return a copy of the metadata. */
+	value = g_new0 (GValue, 1);
+	g_value_init (value, G_TYPE_STRING);
+	g_value_set_string (value, metadata);
+
+	return value;
+}
+
+/* Private signal handler for 'entry-extra-metadata-request::'
+ * RHYTHMDB_PROP_STREAM_SONG_TITLE from RhythmDB. */
+static GValue *
+streaming_title_request_cb (RhythmDB *db, RhythmDBEntry *entry,
+	RBStreamingSignalHandler *handler)
+{
+	return metadata_request_helper (handler, entry, handler->priv->title);
+}
+
+/* Private signal handler for 'entry-extra-metadata-request::'
+ * RHYTHMDB_PROP_STREAM_ARTIST from RhythmDB. */
+static GValue *
+streaming_artist_request_cb (RhythmDB *db, RhythmDBEntry *entry,
+	RBStreamingSignalHandler *handler)
+{
+	/* If we have a streaming title to report, but no artist, force this
+	 * to an empty string. Otherwise "Unknown" will appear in the
+	 * interface. */
+	gchar *metadata = handler->priv->artist;
+	if (handler->priv->title && !metadata) {
+		metadata = "";
+	}
+
+	return metadata_request_helper (handler, entry, metadata);
+}
+
+/* Private signal handler for 'entry-extra-metadata-request::'
+ * RHYTHMDB_PROP_STREAM_SONG_ALBUM from RhythmDB. */
+static GValue *
+streaming_album_request_cb (RhythmDB *db, RhythmDBEntry *entry,
+	RBStreamingSignalHandler *handler)
+{
+	/* If we have a streaming title to report, but no album, force this
+	 * to an empty string. Otherwise "Unknown" will appear in the
+	 * interface. */
+	gchar *metadata = handler->priv->album;
+	if (handler->priv->title && !metadata) {
+		metadata = "";
+	}
+
+	return metadata_request_helper (handler, entry, metadata);
+}
+
+/* Private signal handler for 'entry-extra-metadata-gather' from RhythmDB. */
+static void
+extra_metadata_gather_cb (RhythmDB *db, RhythmDBEntry *entry,
+	RBStringValueMap *value_map, RBStreamingSignalHandler *handler)
+{
+	/* Bail out if validation checks fail. */
+	if (entry != rb_shell_player_get_playing_entry (handler->priv->shell_player) ||
+	    !check_entry_type (handler, entry))
+	{
+		return;
+	}
+
+	/* Propagate all of the metadata we have available. */
+	GValue value = {0,};
+	g_value_init (&value, G_TYPE_STRING);
+
+	if (handler->priv->title) {
+		g_value_set_string (&value, handler->priv->title);
+		rb_string_value_map_set (value_map,
+					 RHYTHMDB_PROP_STREAM_SONG_TITLE, 
+					 &value);
+		g_value_unset (&value);
+	}
+
+	if (handler->priv->artist) {
+		g_value_set_string (&value, handler->priv->artist);
+		rb_string_value_map_set (value_map, 
+					 RHYTHMDB_PROP_STREAM_SONG_ARTIST, 
+					 &value);
+		g_value_unset (&value);
+	}
+
+	if (handler->priv->album) {
+		g_value_set_string (&value, handler->priv->album);
+		rb_string_value_map_set (value_map,
+					 RHYTHMDB_PROP_STREAM_SONG_ALBUM, 
+					 &value);
+		g_value_unset (&value);
+	}
+}
+
+/* Private signal handler for 'playing-source-changed' from RBPlayer. */
+static void
+playing_source_changed_cb (RBShellPlayer *shell_player,
+			   RBSource *source,
+			   RBStreamingSignalHandler *handler)
+{
+	GObject *backend;
+	g_object_get (shell_player, "player", &backend, NULL);
+
+	/* Disconnect if switching between our own sources or to
+	 * somebody else's source. */
+	if (handler->priv->info_available_id) {
+		g_signal_handler_disconnect (backend,
+					     handler->priv->info_available_id);
+		handler->priv->info_available_id = 0;
+	}
+
+	/* Connect if the new source is one of ours. */
+	if (source == handler->priv->source) {
+		handler->priv->info_available_id = g_signal_connect_object ( 
+			backend,
+			"info", G_CALLBACK (info_available_cb),
+			handler, 0);
+	}
+
+	g_object_unref (backend);
+}
+
+/* Private signal handler for 'info' from RBPlayer. */
+static void
+info_available_cb (RBPlayer *backend,
+		   const gchar *uri,
+		   RBMetaDataField field,
+		   GValue *value,
+		   RBStreamingSignalHandler *handler)
+{
+	const gchar *location;
+	gchar *value_str = NULL;
+	RhythmDBEntry *entry;
+	RhythmDBPropType entry_field = 0;
+
+	/* Sanity checks. */
+	if (!rb_player_opened (backend)) {
+		rb_debug ("Got info_available but not playing");
+		return;
+	}
+
+	entry = rb_shell_player_get_playing_entry (handler->priv->shell_player);
+	if (!check_entry_type (handler, entry)) {
+		return;
+	}
+
+	GDK_THREADS_ENTER ();
+
+	/* Ignore metadata that does not originate from a streaming audio
+	 * source. */
+	if (!handler->priv->only_streams) {
+		location = rhythmdb_entry_get_string (entry,
+						      RHYTHMDB_PROP_MOUNTPOINT);
+		if (!location || location[0] == '\0') {
+			goto out_unlock;
+		}
+	}
+
+	/* Check value is a UTF-8 string. */
+	switch (field) {
+	case RB_METADATA_FIELD_TITLE:
+	case RB_METADATA_FIELD_ARTIST:
+	case RB_METADATA_FIELD_ALBUM:
+	case RB_METADATA_FIELD_GENRE:
+	case RB_METADATA_FIELD_COMMENT:
+		value_str = g_value_dup_string (value);
+		if (!g_utf8_validate (value_str, -1, NULL)) {
+			g_warning ("Invalid UTF-8 from audio stream: %s",
+				   value_str);
+			goto out_unlock;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* Collect metadata if feature is enabled. */
+	if (handler->priv->handle_metadata_collection) {
+		switch (field) {
+		case RB_METADATA_FIELD_TITLE:
+			rb_streaming_signal_handler_set_title (handler,
+							       value_str);
+			break;
+		case RB_METADATA_FIELD_ARTIST:
+			rb_streaming_signal_handler_set_artist (handler,
+								value_str);
+			break;
+		case RB_METADATA_FIELD_ALBUM:
+			rb_streaming_signal_handler_set_album (handler,
+							       value_str);
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Update RhythmDB with metadata if feature is enabled. */
+	if (handler->priv->handle_metadata_db_updates) {
+		const gchar *existing, *location;
+
+		switch (field) {
+		case RB_METADATA_FIELD_GENRE:
+			/* Only update DB entry if genre isn't already set. */
+			existing = rhythmdb_entry_get_string (entry,
+							   RHYTHMDB_PROP_GENRE);
+			if (!existing ||
+			    strcmp (existing, "") == 0 ||
+			    strcmp (existing, _("Unknown")) == 0)
+			{
+				entry_field = RHYTHMDB_PROP_GENRE;
+				rb_debug ("Setting genre of streaming audio to "
+					"%s", value_str);
+			} else {
+				rb_debug ("Streaming audio already has genre: "
+					  "%s; ignoring %s",
+					  existing, value_str);
+			}
+			break;
+		case RB_METADATA_FIELD_COMMENT:
+			/* Only update the DB entry if a title isn't already
+			 * set. Consider title==URI to be the same as no title,
+			 * since that's what happens for stations imported by
+			 * DnD or commandline args. If the station title
+			 * really is the same as the URI, then surely the
+			 * station title in the stream metadata will say that
+			 * too. */
+			existing = rhythmdb_entry_get_string (entry,
+							   RHYTHMDB_PROP_TITLE);
+			location = rhythmdb_entry_get_string (entry,
+							RHYTHMDB_PROP_LOCATION);
+			if (!existing ||
+			    strcmp (existing, "") == 0 ||
+			    strcmp (existing, location) == 0)
+			{
+				entry_field = RHYTHMDB_PROP_TITLE;
+				rb_debug ("Setting title of streaming audio to "
+					  "%s", value_str);
+			} else {
+				rb_debug ("Streaming audio already has title: "
+					  "%s; ignoring %s",
+					  existing, value_str);
+			}
+			break;
+		case RB_METADATA_FIELD_BITRATE:
+			if (!rhythmdb_entry_get_ulong (entry,
+			  			       RHYTHMDB_PROP_BITRATE))
+			{
+				gulong bitrate;
+
+				/* Convert GStreamer bitrate (bits) to kbps */
+				bitrate = g_value_get_ulong (value);
+				g_value_set_ulong (value, bitrate / 1000);
+
+				entry_field = RHYTHMDB_PROP_BITRATE;
+				rb_debug ("Setting bitrate of streamign audio "
+					  "to %lu", g_value_get_ulong (value));
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Commit any changes to the database. */
+	if (entry_field != 0) {
+		rhythmdb_entry_set (handler->priv->db,
+				    entry,
+				    entry_field, value);
+		rhythmdb_commit (handler->priv->db);
+	}
+
+ out_unlock:
+ 	GDK_THREADS_LEAVE ();
+
+	g_free (value_str);
+}
+
+/* Private timeout handler for metadata propagation. */
+static gboolean
+emit_notification_cb (RBStreamingSignalHandler *handler)
+{
+	RBShell *shell;
+
+	handler->priv->emit_notification_id = 0;
+	g_object_get (G_OBJECT (handler->priv->source),
+		      "shell", &shell,
+		      NULL);
+	rb_shell_notify_playing_entry (shell,
+				       handler->priv->playing_entry,
+				       FALSE);
+	g_object_unref (shell);
+
+	return FALSE;
+}
+
+/* Private signal handler for 'playing-song-changed' from RBPlayer. */
+static void
+playing_song_changed_cb (RBShellPlayer *player,
+			 RhythmDBEntry *entry,
+			 RBStreamingSignalHandler *handler)
+{
+	GObject *backend;
+
+	if (handler->priv->playing_entry == entry) {
+		return;
+	}
+
+	/* On a new song, remove all of our existing metadata. */
+	g_free (handler->priv->title);
+	g_free (handler->priv->artist);
+	g_free (handler->priv->album);
+	handler->priv->title = NULL;
+	handler->priv->artist = NULL;
+	handler->priv->album = NULL;
+
+	/* Release the previous playing entry. */
+	if (handler->priv->playing_entry) {
+		rb_source_update_play_statistics (
+			RB_SOURCE (handler->priv->source),
+			handler->priv->db,
+			handler->priv->playing_entry);
+		rhythmdb_entry_unref (handler->priv->playing_entry);
+		handler->priv->playing_entry = NULL;
+	}
+
+	g_object_get (handler->priv->shell_player, "player", &backend, NULL);
+
+	if (!check_entry_type (handler, entry)) {
+		/* Clear buffering statistics and don't report any more. */
+		if (handler->priv->handle_progress_maintenance) {
+			handler->priv->buffering = 0;
+			if (handler->priv->buffering_id) {
+				g_signal_handler_disconnect (backend,
+						   handler->priv->buffering_id);
+				handler->priv->buffering_id = 0;
+
+				rb_source_notify_status_changed (RB_SOURCE (handler->priv->source));
+			}
+		}
+	} else {
+		/* Install buffering statistic handler. */
+		if (handler->priv->handle_progress_maintenance) {
+			rb_debug ("Playing new stream; resetting buffering");
+			if (handler->priv->buffering_id == 0) {
+				handler->priv->buffering_id =
+					g_signal_connect_object (backend,
+								 "buffering",
+						      G_CALLBACK (buffering_cb),
+								 handler, 0);
+			}
+
+			handler->priv->buffering = -1;
+		}
+
+		/* Update currently playing stream. */
+		handler->priv->playing_entry = rhythmdb_entry_ref (entry);
+		rb_source_notify_status_changed (RB_SOURCE (handler->priv->source));
+	}
+
+	g_object_unref (backend);
+}
+
+/* Private signal handler for 'buffering' from RBPlayer. */
+static void
+buffering_cb (RBPlayer *player,
+	      gpointer unused,
+	      guint progress,
+	      RBStreamingSignalHandler *handler)
+{
+	/* Bail if we have no progress information. */
+	if (progress == 0) {
+		return;
+	}
+
+	if (progress == 100) {
+		progress = 0;
+	}
+
+	GDK_THREADS_ENTER ();
+	handler->priv->buffering = progress;
+	rb_source_notify_status_changed (RB_SOURCE (handler->priv->source));
+	GDK_THREADS_LEAVE ();
+}
diff --git a/sources/rb-streaming-signal-handler.h b/sources/rb-streaming-signal-handler.h
new file mode 100644
index 0000000..e56f44c
--- /dev/null
+++ b/sources/rb-streaming-signal-handler.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Provides signal handling functionality for streaming audio sources.
+ *  Maintains stream metadata and buffering information.
+ *
+ *  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 of the License, or
+ *  (at your option) any later 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_STREAMING_SIGNAL_HANDLER_H
+#define __RB_STREAMING_SIGNAL_HANDLER_H
+
+#include "rb-shell-player.h"
+#include "rhythmdb.h"
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_STREAMING_SIGNAL_HANDLER	(rb_streaming_signal_handler_get_type ())
+#define RB_STREAMING_SIGNAL_HANDLER(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_STREAMING_SIGNAL_HANDLER, RBStreamingSignalHandler))
+#define RB_STREAMING_SIGNAL_HANDLER_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_STREAMING_SIGNAL_HANDLER, RBStreamingSignalHandlerClass))
+#define RB_IS_STREAMING_SIGNAL_HANDLER(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_STREAMING_SIGNAL_HANDLER))
+#define RB_IS_STREAMING_SIGNAL_HANDLER_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_STREAMING_SIGNAL_HANDLER))
+#define RB_STREAMING_SIGNAL_HANDLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_STREAMING_SIGNAL_HANDLER, RBStreamingSignalHandlerClass))
+
+typedef struct RBStreamingSignalHandlerPrivate RBStreamingSignalHandlerPrivate;
+
+typedef struct
+{
+	GObject parent;
+
+	RBStreamingSignalHandlerPrivate *priv;
+} RBStreamingSignalHandler;
+
+typedef struct
+{
+	GObjectClass parent;
+} RBStreamingSignalHandlerClass;
+
+GType rb_streaming_signal_handler_get_type (void);
+
+/* Initialise a streaming signal handler.
+ *
+ * source:			RBSource for the owner of this handler.
+ * only_streams:		Set FALSE if non-streaming entries are mixed in
+ *				with streaming entries. Uses RHYTHMDB_PROP_
+ *				MOUNTPOINT to distinguish in that case.
+ * handle_metadata_requests:	Respond to title/artist/album metadata requests
+ *				with collected or manually set data.
+ * handle_metadata_collection:	Collect title/artist/album from 'info' signals.
+ * handle_metadata_db_updates:	Collect genre/comment/bitrate from 'info'
+ *				signals and update the RhythmDB entry.
+ * handle_progress_maintenance:	Maintain stream progress information for
+ *				manual inspection.
+ */
+RBStreamingSignalHandler *rb_streaming_signal_handler_new (
+	RBSource *source,
+	gboolean only_streams,
+	gboolean handle_metadata_requests,
+	gboolean handle_metadata_collection,
+	gboolean handle_metadata_db_updates,
+	gboolean handle_progress_maintenance);
+
+/* Manually set elements of streaming metadata to be fed back in the metadata
+ * handler. Requires handle_metadata_requests = TRUE. */
+void rb_streaming_signal_handler_set_title  (RBStreamingSignalHandler *handler, 
+					     gchar *title);
+void rb_streaming_signal_handler_set_artist (RBStreamingSignalHandler *handler, 
+					     gchar *artist);
+void rb_streaming_signal_handler_set_album  (RBStreamingSignalHandler *handler, 
+					     gchar *album);
+
+/* Inspect progress information collected so far. Requires
+ * handle_progress_maintenance = TRUE. */
+void rb_streaming_signal_handler_get_progress (
+	RBStreamingSignalHandler *handler,
+	gchar **progress_text,
+	gfloat *progress_value);
+
+G_END_DECLS
+
+#endif /* __RB_STREAMING_SIGNAL_HANDLER_H */
diff --git a/sources/rb-streaming-source.c b/sources/rb-streaming-source.c
deleted file mode 100644
index 2cc73f4..0000000
--- a/sources/rb-streaming-source.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- *  Copyright (C) 2002,2003 Colin Walters <walters@debian.org>
- *  Copyright (C) 2006 Jonathan Matthew <jonathan@kaolin.wh9.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 of the License, or
- *  (at your option) any later 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.
- *
- */
-
-/*
- * base class for streaming sources (internet radio, last.fm streams)
- * provides handling of:
- *  - buffering signals (done)
- *  - streaming song metadata (done)
- *  - possibly updating play count etc.?
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
-
-#include "rb-streaming-source.h"
-
-#include "rhythmdb-query-model.h"
-#include "rb-util.h"
-#include "rb-file-helpers.h"
-#include "rb-preferences.h"
-#include "rb-dialog.h"
-#include "rb-debug.h"
-#include "rb-shell.h"
-#include "rb-shell-player.h"
-#include "rb-player.h"
-#include "rb-metadata.h"
-
-#define STREAMING_METADATA_NOTIFY_TIMEOUT	350
-
-static void rb_streaming_source_class_init (RBStreamingSourceClass *klass);
-static void rb_streaming_source_init (RBStreamingSource *source);
-static GObject *rb_streaming_source_constructor (GType type, guint n_construct_properties,
-						 GObjectConstructParam *construct_properties);
-static void rb_streaming_source_dispose (GObject *object);
-
-/* source methods */
-static RBSourceEOFType impl_handle_eos (RBSource *asource);
-
-static void playing_entry_changed_cb (RBShellPlayer *player,
-				      RhythmDBEntry *entry,
-				      RBStreamingSource *source);
-static GValue * streaming_title_request_cb (RhythmDB *db,
-					    RhythmDBEntry *entry,
-					    RBStreamingSource *source);
-static GValue * streaming_artist_request_cb (RhythmDB *db,
-					     RhythmDBEntry *entry,
-					     RBStreamingSource *source);
-static GValue * streaming_album_request_cb (RhythmDB *db,
-					    RhythmDBEntry *entry,
-					    RBStreamingSource *source);
-static void extra_metadata_gather_cb (RhythmDB *db,
-				      RhythmDBEntry *entry,
-				      RBStringValueMap *data,
-				      RBStreamingSource *source);
-
-struct RBStreamingSourcePrivate
-{
-	RhythmDB *db;
-
-	gboolean initialized;
-	gboolean is_playing;
-
-	RBShellPlayer *player;
-	RhythmDBEntry *playing_stream;
-	char *streaming_title;
-	char *streaming_artist;
-	char *streaming_album;
-
-	gint emit_notify_id;
-	gint buffering_id;
-	guint buffering;
-
-	gboolean dispose_has_run;
-};
-
-G_DEFINE_TYPE (RBStreamingSource, rb_streaming_source, RB_TYPE_SOURCE)
-
-static void
-rb_streaming_source_class_init (RBStreamingSourceClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
-
-	object_class->dispose = rb_streaming_source_dispose;
-	object_class->constructor = rb_streaming_source_constructor;
-
-	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
-	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
-	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
-	source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
-	source_class->impl_handle_eos = impl_handle_eos;
-	source_class->impl_try_playlist = (RBSourceFeatureFunc) rb_true_function;
-
-	g_type_class_add_private (klass, sizeof (RBStreamingSourcePrivate));
-}
-
-static void
-rb_streaming_source_init (RBStreamingSource *source)
-{
-	source->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((source),
-						     RB_TYPE_STREAMING_SOURCE,
-						     RBStreamingSourcePrivate));
-}
-
-static void
-rb_streaming_source_dispose (GObject *object)
-{
-	RBStreamingSource *source;
-
-	source = RB_STREAMING_SOURCE (object);
-
-	if (source->priv->player) {
-		g_object_unref (source->priv->player);
-		source->priv->player = NULL;
-	}
-
-	if (source->priv->db) {
-		g_object_unref (source->priv->db);
-		source->priv->db = NULL;
-	}
-
-	G_OBJECT_CLASS (rb_streaming_source_parent_class)->dispose (object);
-}
-
-static GObject *
-rb_streaming_source_constructor (GType type,
-			      guint n_construct_properties,
-			      GObjectConstructParam *construct_properties)
-{
-	RBStreamingSource *source;
-	RBStreamingSourceClass *klass;
-	RBShell *shell;
-
-	klass = RB_STREAMING_SOURCE_CLASS (g_type_class_peek (RB_TYPE_STREAMING_SOURCE));
-
-	source = RB_STREAMING_SOURCE (G_OBJECT_CLASS (rb_streaming_source_parent_class)
-			->constructor (type, n_construct_properties, construct_properties));
-
-	g_object_get (G_OBJECT (source), "shell", &shell, NULL);
-	g_object_get (G_OBJECT (shell),
-		      "db", &source->priv->db,
-		      "shell-player", &source->priv->player,
-		      NULL);
-	g_object_unref (shell);
-
-	g_signal_connect_object (G_OBJECT (source->priv->db),
-				 "entry-extra-metadata-request::" RHYTHMDB_PROP_STREAM_SONG_TITLE,
-				 G_CALLBACK (streaming_title_request_cb),
-				 source, 0);
-
-	g_signal_connect_object (G_OBJECT (source->priv->db),
-				 "entry-extra-metadata-request::" RHYTHMDB_PROP_STREAM_SONG_ARTIST,
-				 G_CALLBACK (streaming_artist_request_cb),
-				 source, 0);
-
-	g_signal_connect_object (G_OBJECT (source->priv->db),
-				 "entry-extra-metadata-request::" RHYTHMDB_PROP_STREAM_SONG_ALBUM,
-				 G_CALLBACK (streaming_album_request_cb),
-				 source, 0);
-
-	g_signal_connect_object (G_OBJECT (source->priv->db),
-				 "entry-extra-metadata-gather",
-				 G_CALLBACK (extra_metadata_gather_cb),
-				 source, 0);
-
-/*	g_signal_connect_object (source->priv->player, "playing-source-changed",
-				 G_CALLBACK (playing_source_changed_cb),
-				 source, 0); */
-	g_signal_connect_object (source->priv->player, "playing-song-changed",
-				 G_CALLBACK (playing_entry_changed_cb),
-				 source, 0);
-
-	return G_OBJECT (source);
-}
-
-static RBSourceEOFType
-impl_handle_eos (RBSource *asource)
-{
-	return RB_SOURCE_EOF_RETRY;
-}
-
-void
-rb_streaming_source_get_progress (RBStreamingSource *source, char **text, float *progress)
-{
-	*progress = 0.0;
-	if (source->priv->buffering == -1) {
-		*text = g_strdup (_("Connecting"));
-	} else if (source->priv->buffering > 0) {
-		*progress = ((float)source->priv->buffering)/100;
-		*text = g_strdup (_("Buffering"));
-	}
-}
-
-static void
-buffering_cb (GObject *backend, gpointer whatever, guint progress, RBStreamingSource *source)
-{
-	if (progress == 0)
-		return;
-
-	if (progress == 100)
-		progress = 0;
-
-	GDK_THREADS_ENTER ();
-	source->priv->buffering = progress;
-	rb_source_notify_status_changed (RB_SOURCE (source));
-	GDK_THREADS_LEAVE ();
-}
-
-static gboolean
-check_entry_type (RBStreamingSource *source, RhythmDBEntry *entry)
-{
-	RhythmDBEntryType entry_type;
-	gboolean matches = FALSE;
-
-	g_object_get (source, "entry-type", &entry_type, NULL);
-	if (entry != NULL && rhythmdb_entry_get_entry_type (entry) == entry_type)
-		matches = TRUE;
-	g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
-
-	return matches;
-}
-
-static GValue *
-streaming_title_request_cb (RhythmDB *db,
-			    RhythmDBEntry *entry,
-			    RBStreamingSource *source)
-{
-	GValue *value;
-	if (check_entry_type (source, entry) == FALSE ||
-	    entry != rb_shell_player_get_playing_entry (source->priv->player) ||
-	    source->priv->streaming_title == NULL)
-		return NULL;
-
-	rb_debug ("returning streaming title \"%s\" to extra metadata request", source->priv->streaming_title);
-	value = g_new0 (GValue, 1);
-	g_value_init (value, G_TYPE_STRING);
-	g_value_set_string (value, source->priv->streaming_title);
-	return value;
-}
-
-static GValue *
-streaming_artist_request_cb (RhythmDB *db,
-			     RhythmDBEntry *entry,
-			     RBStreamingSource *source)
-{
-	GValue *value;
-
-	if (check_entry_type (source, entry) == FALSE ||
-	    entry != rb_shell_player_get_playing_entry (source->priv->player) ||
-	    source->priv->streaming_artist == NULL)
-		return NULL;
-
-	rb_debug ("returning streaming artist \"%s\" to extra metadata request", source->priv->streaming_artist);
-	value = g_new0 (GValue, 1);
-	g_value_init (value, G_TYPE_STRING);
-	g_value_set_string (value, source->priv->streaming_artist);
-	return value;
-}
-
-static GValue *
-streaming_album_request_cb (RhythmDB *db,
-			    RhythmDBEntry *entry,
-			    RBStreamingSource *source)
-{
-	GValue *value;
-
-	if (check_entry_type (source, entry) == FALSE ||
-	    entry != rb_shell_player_get_playing_entry (source->priv->player) ||
-	    source->priv->streaming_album == NULL)
-		return NULL;
-
-	rb_debug ("returning streaming album \"%s\" to extra metadata request", source->priv->streaming_album);
-	value = g_new0 (GValue, 1);
-	g_value_init (value, G_TYPE_STRING);
-	g_value_set_string (value, source->priv->streaming_album);
-	return value;
-}
-static void
-extra_metadata_gather_cb (RhythmDB *db,
-			  RhythmDBEntry *entry,
-			  RBStringValueMap *data,
-			  RBStreamingSource *source)
-{
-	/* our extra metadata only applies to the playing entry */
-	if (entry != rb_shell_player_get_playing_entry (source->priv->player) ||
-	    check_entry_type (source, entry) == FALSE)
-		return;
-
-	if (source->priv->streaming_title != NULL) {
-		GValue value = {0,};
-
-		g_value_init (&value, G_TYPE_STRING);
-		g_value_set_string (&value, source->priv->streaming_title);
-		rb_string_value_map_set (data, RHYTHMDB_PROP_STREAM_SONG_TITLE, &value);
-		g_value_unset (&value);
-	}
-
-	if (source->priv->streaming_artist != NULL) {
-		GValue value = {0,};
-
-		g_value_init (&value, G_TYPE_STRING);
-		g_value_set_string (&value, source->priv->streaming_artist);
-		rb_string_value_map_set (data, RHYTHMDB_PROP_STREAM_SONG_ARTIST, &value);
-		g_value_unset (&value);
-	}
-
-	if (source->priv->streaming_album != NULL) {
-		GValue value = {0,};
-
-		g_value_init (&value, G_TYPE_STRING);
-		g_value_set_string (&value, source->priv->streaming_album);
-		rb_string_value_map_set (data, RHYTHMDB_PROP_STREAM_SONG_ALBUM, &value);
-		g_value_unset (&value);
-	}
-}
-
-static gboolean
-emit_notification_cb (RBStreamingSource *source)
-{
-	RBShell *shell;
-
-	source->priv->emit_notify_id = 0;
-
-	g_object_get (G_OBJECT (source), "shell", &shell, NULL);
-	rb_shell_notify_playing_entry (shell,
-				       source->priv->playing_stream,
-				       FALSE);
-	g_object_unref (shell);
-
-	return FALSE;
-}
-
-static void
-set_streaming_metadata (RBStreamingSource *source,
-			char **field,
-			const char *metadata_field,
-			const char *value)
-{
-	GValue v = {0,};
-
-	/* don't do anything if the value isn't changing */
-	if (*field != NULL && strcmp (*field, value) == 0) {
-		return;
-	}
-
-	g_free (*field);
-	*field = g_strdup (value);
-
-	g_value_init (&v, G_TYPE_STRING);
-	g_value_set_string (&v, value);
-	rhythmdb_emit_entry_extra_metadata_notify (source->priv->db,
-						   source->priv->playing_stream,
-						   metadata_field,
-						   &v);
-	g_value_unset (&v);
-
-	if (source->priv->emit_notify_id != 0)
-		g_source_remove (source->priv->emit_notify_id);
-
-	source->priv->emit_notify_id = g_timeout_add (STREAMING_METADATA_NOTIFY_TIMEOUT,
-						      (GSourceFunc) emit_notification_cb,
-						      source);
-}
-
-void
-rb_streaming_source_set_streaming_title (RBStreamingSource *source,
-					 const char *title)
-{
-	rb_debug ("streaming title: \"%s\"", title);
-	set_streaming_metadata (source,
-				&source->priv->streaming_title,
-				RHYTHMDB_PROP_STREAM_SONG_TITLE,
-				title);
-}
-
-void
-rb_streaming_source_set_streaming_artist (RBStreamingSource *source,
-					  const char *artist)
-{
-	rb_debug ("streaming artist: \"%s\"", artist);
-	set_streaming_metadata (source,
-				&source->priv->streaming_artist,
-				RHYTHMDB_PROP_STREAM_SONG_ARTIST,
-				artist);
-}
-
-void
-rb_streaming_source_set_streaming_album (RBStreamingSource *source,
-					 const char *album)
-{
-	rb_debug ("streaming album: \"%s\"", album);
-	set_streaming_metadata (source,
-				&source->priv->streaming_album,
-				RHYTHMDB_PROP_STREAM_SONG_ALBUM,
-				album);
-}
-
-
-static void
-playing_entry_changed_cb (RBShellPlayer *player,
-			  RhythmDBEntry *entry,
-			  RBStreamingSource *source)
-{
-	GObject *backend;
-
-	if (source->priv->playing_stream == entry)
-		return;
-
-	g_free (source->priv->streaming_title);
-	g_free (source->priv->streaming_artist);
-	g_free (source->priv->streaming_album);
-	source->priv->streaming_title = NULL;
-	source->priv->streaming_artist = NULL;
-	source->priv->streaming_album = NULL;
-
-	if (source->priv->playing_stream != NULL) {
-		rb_source_update_play_statistics (RB_SOURCE (source),
-						  source->priv->db,
-						  source->priv->playing_stream);
-
-		rhythmdb_entry_unref (source->priv->playing_stream);
-		source->priv->playing_stream = NULL;
-	}
-
-	g_object_get (source->priv->player, "player", &backend, NULL);
-
-	if (check_entry_type (source, entry) == FALSE) {
-		source->priv->buffering = 0;
-		if (source->priv->buffering_id) {
-			g_signal_handler_disconnect (backend,
-						     source->priv->buffering_id);
-			source->priv->buffering_id = 0;
-
-			rb_source_notify_status_changed (RB_SOURCE (source));
-		}
-	} else {
-		rb_debug ("playing new stream; resetting buffering");
-		if (source->priv->buffering_id == 0) {
-			source->priv->buffering_id =
-				g_signal_connect_object (backend, "buffering",
-							 G_CALLBACK (buffering_cb),
-							 source, 0);
-		}
-
-		source->priv->buffering = -1;
-
-		source->priv->playing_stream = rhythmdb_entry_ref (entry);
-		rb_source_notify_status_changed (RB_SOURCE (source));
-	}
-
-	g_object_unref (backend);
-}
-
diff --git a/sources/rb-streaming-source.h b/sources/rb-streaming-source.h
deleted file mode 100644
index e8956e0..0000000
--- a/sources/rb-streaming-source.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  Copyright (C) 2006 Jonathan Matthew <jonathan@kaolin.wh9.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 of the License, or
- *  (at your option) any later 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_STREAMING_SOURCE_H
-#define __RB_STREAMING_SOURCE_H
-
-#include "rb-source.h"
-
-G_BEGIN_DECLS
-
-#define RB_TYPE_STREAMING_SOURCE         (rb_streaming_source_get_type ())
-#define RB_STREAMING_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_STREAMING_SOURCE, RBStreamingSource))
-#define RB_STREAMING_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_STREAMING_SOURCE, RBStreamingSourceClass))
-#define RB_IS_STREAMING_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_STREAMING_SOURCE))
-#define RB_IS_STREAMING_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_STREAMING_SOURCE))
-#define RB_STREAMING_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_STREAMING_SOURCE, RBStreamingSourceClass))
-
-typedef struct RBStreamingSourcePrivate RBStreamingSourcePrivate;
-
-typedef struct
-{
-	RBSource parent;
-
-	RBStreamingSourcePrivate *priv;
-} RBStreamingSource;
-
-typedef struct
-{
-	RBSourceClass parent;
-} RBStreamingSourceClass;
-
-GType		rb_streaming_source_get_type	(void);
-
-/* methods for subclasses */
-void		rb_streaming_source_get_progress (RBStreamingSource *source,
-						  char **progress_text,
-						  float *progress);
-void		rb_streaming_source_set_streaming_title (RBStreamingSource *source,
-							 const char *title);
-void		rb_streaming_source_set_streaming_artist (RBStreamingSource *source,
-							  const char *artist);
-void		rb_streaming_source_set_streaming_album (RBStreamingSource *source,
-							 const char *album);
-
-G_END_DECLS
-
-#endif /* __RB_STREAMING_SOURCE_H */
-
