? player/rb-player-gst (copy).c
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/rhythmbox/ChangeLog,v
retrieving revision 1.1170
diff -u -r1.1170 ChangeLog
--- ChangeLog	30 Aug 2005 21:08:14 -0000	1.1170
+++ ChangeLog	31 Aug 2005 11:25:18 -0000
@@ -1,3 +1,38 @@
+2005-08-31  Raphael Slinckx  <rslinckx@cvs.gnome.org>
+
+	* acinclude.m4:
+	* configure.ac:
+	* data/.cvsignore:
+	* data/Makefile.am:
+	* data/art/Makefile.am:
+	* data/art/rhythmbox-buddies.png:
+	* lib/rb-stock-icons.c: (rb_stock_icons_init):
+	* lib/rb-stock-icons.h:
+	* player/rb-player-gst.c: (rb_player_finalize),
+	(rb_player_gst_free_playbin), (eos_cb), (rb_player_construct),
+	(rb_player_sync_pipeline), (rb_player_set_time):
+	* shell/Makefile.am:
+	* shell/rb-shell-player.c: (rb_shell_player_entry_changed_cb):
+	* shell/rb-shell.c: (rb_shell_jump_to_entry_with_source):
+	* sources/.cvsignore:
+	* sources/Makefile.am:
+	* sources/rb-buddymusic-source.c: (rb_buddymusic_source_get_type),
+	(rhythmdb_entry_buddymusic_get_type),
+	(rb_buddymusic_source_class_init), (rb_buddymusic_source_init),
+	(rb_buddymusic_source_constructor),
+	(rb_buddymusic_source_finalize), (rb_buddymusic_source_new),
+	(rb_buddymusic_source_get_icon_cell_data),
+	(rb_buddymusic_source_get_alias_cell_data),
+	(rb_buddymusic_source_get_entry_view),
+	(rb_buddymusic_source_get_status),
+	(rb_buddymusic_source_get_pixbuf), (rb_buddymusic_source_do_query),
+	(rb_buddymusic_source_add_buddy),
+	(rb_buddymusic_source_update_buddy),
+	(rb_buddymusic_source_remove_buddy),
+	(rb_buddymusic_source_play_stream):
+	* sources/rb-buddymusic-source.h:
+	* sources/rb-buddymusic-source.xml:
+
 2005-08-30  Colin Walters  <walters@verbum.org>
 
 	* shell/rb-shell-preferences.c (rb_shell_preferences_append_view_page): 
Index: acinclude.m4
===================================================================
RCS file: acinclude.m4
diff -N acinclude.m4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ acinclude.m4	31 Aug 2005 11:25:18 -0000
@@ -0,0 +1,40 @@
+dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
+dnl
+dnl example
+dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
+dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
+
+AC_DEFUN([AS_AC_EXPAND],
+[
+  EXP_VAR=[$1]
+  FROM_VAR=[$2]
+
+  dnl first expand prefix and exec_prefix if necessary
+  prefix_save=$prefix
+  exec_prefix_save=$exec_prefix
+
+  dnl if no prefix given, then use /usr/local, the default prefix
+  if test "x$prefix" = "xNONE"; then
+    prefix=$ac_default_prefix
+  fi
+  dnl if no exec_prefix given, then use prefix
+  if test "x$exec_prefix" = "xNONE"; then
+    exec_prefix=$prefix
+  fi
+
+  full_var="$FROM_VAR"
+  dnl loop until it doesn't change anymore
+  while true; do
+    new_full_var="`eval echo $full_var`"
+    if test "x$new_full_var"="x$full_var"; then break; fi
+    full_var=$new_full_var
+  done
+
+  dnl clean up
+  full_var=$new_full_var
+  AC_SUBST([$1], "$full_var")
+
+  dnl restore prefix and exec_prefix
+  prefix=$prefix_save
+  exec_prefix=$exec_prefix_save
+])
Index: configure.ac
===================================================================
RCS file: /cvs/gnome/rhythmbox/configure.ac,v
retrieving revision 1.156
diff -u -r1.156 configure.ac
--- configure.ac	28 Aug 2005 14:56:06 -0000	1.156
+++ configure.ac	31 Aug 2005 11:25:18 -0000
@@ -382,6 +382,12 @@
 fi	
 AM_CONDITIONAL(WITH_BONOBO, test "x$enable_bonobo" = "xyes")
 	      
+dnl DBUS Service files dir
+AS_AC_EXPAND(DATADIR, $datadir)
+DBUS_SERVICES_DIR="$DATADIR/dbus-1/services"
+AC_SUBST(DBUS_SERVICES_DIR)
+AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [Where services dir for DBUS is])
+
 AM_GCONF_SOURCE_2
 
 AC_ARG_ENABLE(uninstalled-build,
Index: data/.cvsignore
===================================================================
RCS file: /cvs/gnome/rhythmbox/data/.cvsignore,v
retrieving revision 1.5
diff -u -r1.5 .cvsignore
--- data/.cvsignore	28 Aug 2005 15:42:08 -0000	1.5
+++ data/.cvsignore	31 Aug 2005 11:25:18 -0000
@@ -7,3 +7,4 @@
 rhythmbox.pc
 rhythmbox.keys
 .arch-ids
+org.gnome.Rhythmbox.service
Index: data/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/data/Makefile.am,v
retrieving revision 1.23
diff -u -r1.23 Makefile.am
--- data/Makefile.am	28 Aug 2005 14:56:06 -0000	1.23
+++ data/Makefile.am	31 Aug 2005 11:25:18 -0000
@@ -57,6 +57,7 @@
 	     $(applications_DATA)   \
 	     $(schema_DATA)	    \
 	     $(iradio_DATA)	    \
+	     $(service_in_files) 	\
 		generate-keys.sh
 
-CLEANFILES = rhythmbox.desktop rhythmbox.keys GNOME_Rhythmbox.server
+CLEANFILES = rhythmbox.desktop rhythmbox.keys GNOME_Rhythmbox.server $(service_DATA)
Index: data/art/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/data/art/Makefile.am,v
retrieving revision 1.20
diff -u -r1.20 Makefile.am
--- data/art/Makefile.am	8 Feb 2005 19:04:38 -0000	1.20
+++ data/art/Makefile.am	31 Aug 2005 11:25:18 -0000
@@ -24,6 +24,7 @@
 	   rhythmbox-audiocd.png       \
 	   rhythmbox-dnd.png           \
 	   rhythmbox-ipod.png	       \
+	   rhythmbox-buddies.png	       \
 	   about-logo.png
 
 artdir = $(pkgdatadir)/art
Index: data/art/rhythmbox-buddies.png
===================================================================
RCS file: data/art/rhythmbox-buddies.png
diff -N data/art/rhythmbox-buddies.png
Binary files /dev/null and rhythmbox-buddies.png differ
Index: lib/rb-stock-icons.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/lib/rb-stock-icons.c,v
retrieving revision 1.17
diff -u -r1.17 rb-stock-icons.c
--- lib/rb-stock-icons.c	8 Feb 2005 19:04:39 -0000	1.17
+++ lib/rb-stock-icons.c	31 Aug 2005 11:25:19 -0000
@@ -60,7 +60,8 @@
 		RB_STOCK_NO_STAR,
 		RB_STOCK_DND_ICON,
                 RB_STOCK_AUDIOCD,
-		RB_STOCK_IPOD
+		RB_STOCK_IPOD,
+		RB_STOCK_BUDDIES
 	};
 
 	g_return_if_fail (factory == NULL);
Index: lib/rb-stock-icons.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/lib/rb-stock-icons.h,v
retrieving revision 1.17
diff -u -r1.17 rb-stock-icons.h
--- lib/rb-stock-icons.h	8 Feb 2005 19:04:39 -0000	1.17
+++ lib/rb-stock-icons.h	31 Aug 2005 11:25:19 -0000
@@ -50,6 +50,7 @@
 #define RB_STOCK_AUDIOCD       "rhythmbox-audiocd"
 #define RB_STOCK_DND_ICON      "rhythmbox-dnd"
 #define RB_STOCK_IPOD          "rhythmbox-ipod"
+#define RB_STOCK_BUDDIES       "rhythmbox-buddies"
 
 void	rb_stock_icons_init	(void);
 
Index: player/rb-player-gst.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/player/rb-player-gst.c,v
retrieving revision 1.42
diff -u -r1.42 rb-player-gst.c
--- player/rb-player-gst.c	26 Aug 2005 23:09:33 -0000	1.42
+++ player/rb-player-gst.c	31 Aug 2005 11:25:20 -0000
@@ -46,6 +46,7 @@
 	char *uri;
 
 	GstElement *playbin;
+	GstElement *server_thread;
 
 	gboolean can_signal_direct_error;
 	GError *error;
@@ -189,6 +190,9 @@
 		gst_element_set_state (mp->priv->playbin,
 				       GST_STATE_NULL);
 		
+		gst_element_set_state (mp->priv->server_thread,
+				       GST_STATE_NULL);
+				       
 		rb_player_gst_free_playbin (mp);
 	}
 
@@ -205,6 +209,12 @@
 	
 	gst_object_unref (GST_OBJECT (player->priv->playbin));
 	player->priv->playbin = NULL;
+	
+	if (player->priv->server_thread == NULL)
+		return;
+
+	gst_object_unref (GST_OBJECT (player->priv->server_thread));
+	player->priv->server_thread = NULL;
 }
 
 static void
@@ -282,6 +292,7 @@
 						 (GSourceFunc) emit_signal_idle,
 						 signal,
 						 destroy_idle_signal);
+//	gst_element_set_state (mp->priv->server_thread, GST_STATE_PAUSED);
 }
 
 static void
@@ -411,12 +422,13 @@
 rb_player_construct (RBPlayer *mp, GError **error)
 {
 	char *element_name = NULL;
-	GstElement *sink;
+	GstElement *gconf, *bin, *tee, *nowplaying, *tcpserver, *queue;
 
 	/* playbin */
 	rb_debug ("constructing element \"playbin\"");
 	mp->priv->playbin = gst_element_factory_make ("playbin", "playbin");
 	if (mp->priv->playbin == NULL) {
+		element_name = "playbin";
 		goto missing_element;
 	}
 	g_signal_connect_object (G_OBJECT (mp->priv->playbin),
@@ -436,18 +448,83 @@
 					 G_CALLBACK (error_cb),
 					 mp, 0);
 
-	/* Output sink */
-	sink = gst_gconf_get_default_audio_sink ();
-	if (sink == NULL) {
-		g_set_error (error,
-			     RB_PLAYER_ERROR,
-			     RB_PLAYER_ERROR_NO_AUDIO,
-			     _("Could not create audio output element; check your settings"));
-		rb_player_gst_free_playbin (mp);
-		return FALSE;
+	bin = gst_bin_new ("bin");
+	if (bin == NULL) {
+		element_name = "bin";
+		goto missing_element;
 	}
+	
+	tee = gst_element_factory_make ("tee", "tee");
+	if (tee == NULL) {
+		element_name = "tee";
+		goto missing_element;
+	}
+	
+	nowplaying = gst_element_factory_make ("nowplaying", "nowplaying");
+	if (nowplaying == NULL) {
+		rb_debug ("No gst element nowplaying, not using it..");
+	}
+	
+	tcpserver = gst_element_factory_make ("tcpserversink", "tcpserversink");
+	if (tcpserver == NULL) {
+		element_name = "tcpserversink";
+		goto missing_element;
+	}
+	g_object_set (G_OBJECT (tcpserver), 
+		"socket-type", 1,
+		"address", "/tmp/rb-buddymusic",
+		"protocol", 1,
+		"sync-method", 0,
+		"timeout", 60*GST_SECOND,
+		"recover-policy", 0,
+		NULL);
+	
+	queue = gst_element_factory_make ("queue", "queue");
+	if (queue == NULL) {
+		element_name = "queue";
+		goto missing_element;
+	}
+	
+	mp->priv->server_thread = gst_thread_new ("serverthread");
+	if (mp->priv->server_thread == NULL) {
+		element_name = "thread";
+		goto missing_element;
+	}
+	
+	gconf = gst_gconf_get_default_audio_sink ();
+	if (gconf == NULL) {
+		element_name = "gconfaudiosink";
+		goto missing_element;
+	}
+	
+	gst_bin_add (GST_BIN (bin), tee);
+	gst_bin_add (GST_BIN (bin), queue);
+	gst_bin_add (GST_BIN (bin), gconf);
+
+	gst_element_link (tee, gconf);
+	gst_element_link (tee, queue);		
+
+	gst_element_add_ghost_pad (bin, gst_element_get_pad (tee, "sink"), "sink");
 
-	g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", sink, NULL);
+	/* Setup tcpserver thread */
+	if (nowplaying != NULL) {
+		gst_bin_add (GST_BIN (mp->priv->server_thread), nowplaying);
+	}
+	gst_bin_add (GST_BIN (mp->priv->server_thread), tcpserver);
+	
+	if (nowplaying != NULL)
+	{	
+		gst_element_link (queue, nowplaying);
+		gst_element_link (nowplaying, tcpserver);
+	}
+	else
+	{
+		gst_element_link (queue, tcpserver);
+	}
+	
+	gst_element_set_state (mp->priv->server_thread, GST_STATE_PLAYING);
+	
+	g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", bin, NULL);
 	g_signal_connect_object (G_OBJECT (mp->priv->playbin), "eos",
 				 G_CALLBACK (eos_cb), mp, 0);
 
@@ -511,12 +588,16 @@
 	if (mp->priv->playing) {
  		rb_debug ("PLAYING pipeline");
  		if (gst_element_set_state (mp->priv->playbin,
+ 					   GST_STATE_PLAYING) == GST_STATE_FAILURE ||
+ 			gst_element_set_state (mp->priv->server_thread,
  					   GST_STATE_PLAYING) == GST_STATE_FAILURE) {
  			return FALSE;
 		}
 	} else {
 		rb_debug ("PAUSING pipeline");
 		if (gst_element_set_state (mp->priv->playbin,
+					   GST_STATE_PAUSED) == GST_STATE_FAILURE ||
+			gst_element_set_state (mp->priv->server_thread,
 					   GST_STATE_PAUSED) == GST_STATE_FAILURE) {
 			return FALSE;
 		}
@@ -764,15 +845,18 @@
 	g_return_if_fail (mp->priv->playbin != NULL);
 
 	gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
-
+	gst_element_set_state (mp->priv->server_thread, GST_STATE_PAUSED);
+	
 	gst_element_seek (mp->priv->playbin, 
 			  GST_FORMAT_TIME 
 			  | GST_SEEK_METHOD_SET 
 			  | GST_SEEK_FLAG_FLUSH, 
 			  time * GST_SECOND);
 
-	if (mp->priv->playing)
+	if (mp->priv->playing) {
 		gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+		gst_element_set_state (mp->priv->server_thread, GST_STATE_PLAYING);
+	}
 }
 
 long
Index: shell/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/Makefile.am,v
retrieving revision 1.54
diff -u -r1.54 Makefile.am
--- shell/Makefile.am	29 Aug 2005 21:08:29 -0000	1.54
+++ shell/Makefile.am	31 Aug 2005 11:25:21 -0000
@@ -21,10 +21,9 @@
 
 tab_files = AUTHORS.tab MAINTAINERS.tab MAINTAINERS.old.tab DOCUMENTERS.tab
 
-bin_PROGRAMS = rhythmbox 
+noinst_LTLIBRARIES = librbshell.la
 
-rhythmbox_SOURCES =              			\
-	main.c						\
+librbshell_la_SOURCES =              			\
 	rb-shell.c					\
 	rb-shell.h					\
 	rb-shell-player.c				\
@@ -64,7 +63,7 @@
 	rb-tray-icon.c					\
 	rb-tray-icon.h				
 
-rhythmbox_LDADD =					\
+librbshell_la_LIBADD =					\
 	$(top_builddir)/sources/libsources.la	        \
 	$(top_builddir)/sources/libsourcesimpl.la	\
 	$(top_builddir)/rhythmdb/librhythmdb.la         \
@@ -80,25 +79,24 @@
 	$(RHYTHMBOX_LIBS)
 
 if WITH_BONOBO
-rhythmbox_LDADD += $(top_builddir)/remote/bonobo/librbbonobo.la
+librbshell_la_LIBADD += $(top_builddir)/remote/bonobo/librbbonobo.la
 endif
 
 if WITH_DBUS
 rb-shell-glue.h: rb-shell.xml Makefile
-	libtool --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell --mode=glib-server --output=$@ $<
+	$(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell --mode=glib-server $< > $@
 rb-shell-player-glue.h: rb-shell-player.xml Makefile
-	libtool --mode=execute $(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell_player --mode=glib-server --output=$@ $<
+	$(DBUS_GLIB_BIN)/dbus-binding-tool --prefix=rb_shell_player --mode=glib-server $< > $@
 
 BUILT_SOURCES += rb-shell-glue.h rb-shell-player-glue.h
 
-rhythmbox_LDADD += $(DBUS_LIBS)
-
+librbshell_la_LIBADD += $(DBUS_LIBS)
 endif
 
 # i hate link order problems
-rhythmbox_LDADD += $(top_builddir)/remote/librbremote.la
+librbshell_la_LIBADD += $(top_builddir)/remote/librbremote.la
 
-rhythmbox_LDFLAGS = -export-dynamic
+librbshell_la_LDFLAGS = -export-dynamic
 
 INCLUDES = 						\
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
@@ -127,12 +125,14 @@
 if WITH_BONOBO
 INCLUDES += -I$(top_srcdir)/remote/bonobo
 endif
-if WITH_DBUS
-INCLUDES += -I$(top_srcdir)/remote/dbus
-endif
+
 if USE_CD_BURNER
-rhythmbox_LDADD += $(LIBNAUTILUS_BURN_LIBS)
+librbshell_la_LIBADD += $(LIBNAUTILUS_BURN_LIBS)
 endif
+
+bin_PROGRAMS = rhythmbox 
+rhythmbox_SOURCES = main.c
+rhythmbox_LDADD = librbshell.la
 
 BUILT_SOURCES += $(tab_files)
 
Index: shell/rb-shell.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-shell.c,v
retrieving revision 1.325
diff -u -r1.325 rb-shell.c
--- shell/rb-shell.c	30 Aug 2005 21:03:28 -0000	1.325
+++ shell/rb-shell.c	31 Aug 2005 11:25:23 -0000
@@ -68,6 +68,7 @@
 #ifdef WITH_IPOD_SUPPORT
 #include "rb-ipod-source.h"
 #endif /* WITH_IPOD_SUPPORT */
+#include "rb-buddymusic-source.h"
 #include "rb-load-failure-dialog.h"
 #include "rb-new-station-dialog.h"
 #include "rb-iradio-source.h"
@@ -316,6 +317,7 @@
 	RBLibrarySource *library_source;
 	RBIRadioSource *iradio_source;
 	RBiPodSource *ipod_source;
+	RBBuddyMusicSource *buddymusic_source;
 
 #ifdef HAVE_AUDIOCD
  	MonkeyMediaAudioCD *cd;
@@ -953,6 +955,8 @@
 	rb_shell_append_source (shell, RB_SOURCE (shell->priv->library_source));
 	shell->priv->iradio_source = RB_IRADIO_SOURCE (rb_iradio_source_new (shell));
 	rb_shell_append_source (shell, RB_SOURCE (shell->priv->iradio_source));
+	shell->priv->buddymusic_source = RB_BUDDYMUSIC_SOURCE (rb_buddymusic_source_new (shell));
+	rb_shell_append_source (shell, RB_SOURCE (shell->priv->buddymusic_source));
 #ifdef WITH_IPOD_SUPPORT
 	shell->priv->ipod_source = RB_IPOD_SOURCE (rb_ipod_source_new (shell));
 	rb_shell_append_source (shell, RB_SOURCE (shell->priv->ipod_source));
Index: sources/.cvsignore
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/.cvsignore,v
retrieving revision 1.2
diff -u -r1.2 .cvsignore
--- sources/.cvsignore	1 Sep 2003 04:21:15 -0000	1.2
+++ sources/.cvsignore	31 Aug 2005 11:25:23 -0000
@@ -1,3 +1,4 @@
 Makefile
 Makefile.in
 .arch-ids
+rb-buddymusic-source-dbus.h
Index: sources/Makefile.am
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/Makefile.am,v
retrieving revision 1.21
diff -u -r1.21 Makefile.am
--- sources/Makefile.am	29 Aug 2005 21:08:23 -0000	1.21
+++ sources/Makefile.am	31 Aug 2005 11:25:24 -0000
@@ -13,11 +13,14 @@
 	rb-library-source.h		\
 	rb-iradio-source.c		\
 	rb-iradio-source.h		\
+	rb-buddymusic-source.c	\
+	rb-buddymusic-source.h	\
+	rb-buddymusic-source-dbus.h \
 	rb-playlist-source.c            \
 	rb-playlist-source.h		
 
 INCLUDES =						\
-        -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
 	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
 	-I$(top_srcdir) 				\
 	-I$(top_srcdir)/lib 				\
@@ -29,17 +32,24 @@
 	-I$(top_srcdir)/library 			\
 	-I$(top_srcdir)/player	 			\
 	-I$(top_srcdir)/iradio				\
+	-I$(top_srcdir)/remote				\
 	-I$(top_srcdir)/shell				\
 	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
 	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
 	-DDATADIR=\""$(datadir)"\"			\
 	$(WARN_CFLAGS)					\
+	$(DBUS_CFLAGS)					\
 	$(TOTEM_PLPARSER_CFLAGS)			\
 	$(HAL_CFLAGS)					\
 	$(RHYTHMBOX_CFLAGS)
 
 libsources_la_LDFLAGS = -export-dynamic
 libsourcesimpl_la_LDFLAGS = -export-dynamic
+libsourcesimpl_la_LIBADD = $(DBUS_LIBS)
+
+EXTRA_DIST = rb-buddymusic-source.xml
+BUILT_SOURCES = rb-buddymusic-source-dbus.h
+CLEANFILES = $(BUILT_SOURCES)
 
 if USE_IPOD
 libsourcesimpl_la_SOURCES +=         \
@@ -57,3 +67,5 @@
 INCLUDES += $(LIBNAUTILUS_BURN_CFLAGS)
 endif
 
+rb-buddymusic-source-dbus.h :  rb-buddymusic-source.xml
+	dbus-binding-tool --prefix=rb_buddymusic_source --mode=glib-server $< > $@
Index: sources/rb-buddymusic-source.c
===================================================================
RCS file: sources/rb-buddymusic-source.c
diff -N sources/rb-buddymusic-source.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sources/rb-buddymusic-source.c	31 Aug 2005 11:25:24 -0000
@@ -0,0 +1,650 @@
+/* 
+ *  For use with gShrooms, provide a buddy music source to rhythmbox
+ *
+ *  Copyright (C) 2005 Raphael Slinckx <raphael@slinckx.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "rb-buddymusic-source.h"
+
+#include "rb-buddymusic-source-dbus.h"
+
+#include "rb-entry-view.h"
+#include "rb-debug.h"
+#include "rb-stock-icons.h"
+#include "rb-util.h"
+#include "rb-preferences.h"
+#include "rb-remote-proxy.h"
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.h>
+#include <string.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#define CONF_STATE_BUDDYMUSIC_SORTING CONF_PREFIX "/state/buddymusic/sorting"
+
+#define RHYTHMDB_ENTRY_TYPE_BUDDYMUSIC (rhythmdb_entry_buddymusic_get_type ())
+#define RB_BUDDYMUSIC_SOURCE_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_BUDDYMUSIC_SOURCE, RBBuddyMusicSourcePrivate))
+
+struct _RBBuddyMusicSourcePrivate {
+	RhythmDB *db;
+	RBEntryView *buddies;
+	
+	GdkPixbuf *icon;
+	
+	GHashTable *buddyuris;
+	GHashTable *buddyicons;
+};
+
+enum {
+	NEED_BUDDIES_SIGNAL,
+	LAST_SIGNAL,
+};
+static guint object_signals[LAST_SIGNAL] = {0};
+
+static RhythmDBEntryType rhythmdb_entry_buddymusic_get_type  (void);
+
+static void rb_buddymusic_source_class_init (RBBuddyMusicSourceClass *klass);
+static void rb_buddymusic_source_init (RBBuddyMusicSource *source);
+static GObject *rb_buddymusic_source_constructor (GType type,
+								guint n_construct_properties,
+								GObjectConstructParam *construct_properties);
+static void rb_buddymusic_source_finalize (GObject *object);
+
+static void
+rb_buddymusic_source_get_alias_cell_data(GtkTreeViewColumn *column,
+								GtkCellRenderer *renderer,
+								GtkTreeModel *tree_model,
+								GtkTreeIter *iter,
+								RBBuddyMusicSource *source);
+static void
+rb_buddymusic_source_get_icon_cell_data(GtkTreeViewColumn *column,
+								GtkCellRenderer *renderer,
+								GtkTreeModel *tree_model,
+								GtkTreeIter *iter,
+								RBBuddyMusicSource *source);
+																
+static RBEntryView *rb_buddymusic_source_get_entry_view (RBSource *asource);
+static char *rb_buddymusic_source_get_status (RBSource *asource);
+static GdkPixbuf *rb_buddymusic_source_get_pixbuf (RBSource *asource);
+
+static void rb_buddymusic_source_do_query (RBBuddyMusicSource *source);
+
+static GObjectClass *parent_class = NULL;
+
+GType
+rb_buddymusic_source_get_type (void)
+{
+	static GType rb_buddymusic_source_type = 0;
+	if (G_UNLIKELY (rb_buddymusic_source_type == 0))
+	{
+		static const GTypeInfo our_info =
+		{
+			sizeof (RBBuddyMusicSourceClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) rb_buddymusic_source_class_init,
+			NULL,
+			NULL,
+			sizeof (RBBuddyMusicSource),
+			0,
+			(GInstanceInitFunc) rb_buddymusic_source_init
+		};
+		rb_buddymusic_source_type = g_type_register_static (RB_TYPE_SOURCE,
+							      "RBBuddyMusicSource",
+							      &our_info, 0);
+	}
+	return rb_buddymusic_source_type;
+}
+
+RhythmDBEntryType
+rhythmdb_entry_buddymusic_get_type (void) 
+{
+	static RhythmDBEntryType buddymusic_type = -1;
+	if (G_UNLIKELY (buddymusic_type == -1)) {
+		buddymusic_type = rhythmdb_entry_register_type ();
+	}
+	return buddymusic_type;
+}
+
+static void
+rb_buddymusic_source_class_init (RBBuddyMusicSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+	GError *error = NULL;
+	
+	parent_class = g_type_class_peek_parent (klass);
+  	g_type_class_add_private (klass, sizeof (RBBuddyMusicSourcePrivate));
+  	
+  	/* GObject methods */
+	object_class->constructor = rb_buddymusic_source_constructor;
+	object_class->finalize = rb_buddymusic_source_finalize;
+	
+	/* rb-source implementation */
+	source_class->impl_get_entry_view = rb_buddymusic_source_get_entry_view;
+	source_class->impl_get_status  = rb_buddymusic_source_get_status;
+	source_class->impl_get_pixbuf  = rb_buddymusic_source_get_pixbuf;
+	
+	/* rb-source capabilities */
+	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;	
+	source_class->impl_have_url = (RBSourceFeatureFunc) rb_true_function;
+	
+	/* DBus Signal */
+	object_signals[NEED_BUDDIES_SIGNAL] =
+		g_signal_new ("need-buddies",
+			G_TYPE_FROM_CLASS (object_class),
+			(GSignalFlags)(G_SIGNAL_RUN_LAST),
+			G_STRUCT_OFFSET (RBBuddyMusicSourceClass, need_buddies),
+			NULL, NULL,
+			g_cclosure_marshal_VOID__VOID,
+			G_TYPE_NONE, 0);
+                                             
+	/* Init the DBus connection, per-klass */
+	klass->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+	if (klass->connection == NULL)
+	{
+		g_warning("Unable to connect to dbus: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+	
+	dbus_g_object_type_install_info (RB_TYPE_BUDDYMUSIC_SOURCE, &dbus_glib_rb_buddymusic_source_object_info);
+}
+
+static void
+rb_buddymusic_source_init (RBBuddyMusicSource *source)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RBBuddyMusicSourceClass *klass = RB_BUDDYMUSIC_SOURCE_GET_CLASS (source);
+	GtkWidget *dummy;
+	GError *error = NULL;
+	DBusGProxy *driver_proxy;
+	guint request_name_ret;
+	
+	/* This hashtable contains (buddy-address, feed url) mappings */
+	priv->buddyuris = g_hash_table_new_full (g_str_hash, g_str_equal,
+										g_free, g_free);
+    
+    /* This hashtable contains (icon filename, buddy pixbuf) mappings */
+	priv->buddyicons = g_hash_table_new_full (g_str_hash, g_str_equal,
+										g_free, g_object_unref);
+										                   
+	/* Create the buddy source icon */
+	dummy = gtk_tree_view_new ();
+	priv->icon = gtk_widget_render_icon (dummy,
+										RB_STOCK_BUDDIES,
+										GTK_ICON_SIZE_LARGE_TOOLBAR,
+										NULL);
+	gtk_widget_destroy (dummy);
+    
+	/* Register DBUS path */
+	dbus_g_connection_register_g_object (klass->connection,
+										"/org/gnome/Rhythmbox/BuddyMusic",
+										G_OBJECT (source));
+	
+	/* Register the service name */
+    driver_proxy = dbus_g_proxy_new_for_name (klass->connection,
+										DBUS_SERVICE_DBUS,
+										DBUS_PATH_DBUS,
+										DBUS_INTERFACE_DBUS);
+
+	if(!org_freedesktop_DBus_request_name (driver_proxy,
+										"org.gnome.Rhythmbox.BuddyMusic",
+										0, &request_name_ret,
+										&error))
+    {
+		if (error != NULL) {
+			g_warning("Unable to register service: %s", error->message);
+			g_error_free (error);
+		 }
+		 else {
+		 	g_warning("Unable to register dbus service");
+		 }
+    }
+    g_object_unref (driver_proxy);
+}
+
+static GObject *
+rb_buddymusic_source_constructor (GType type, guint n_construct_properties,
+			      GObjectConstructParam *construct_properties)
+{
+	RBBuddyMusicSource *source;
+	RBBuddyMusicSourceClass *klass;
+	RBBuddyMusicSourcePrivate *priv;
+	GObjectClass *parent_class;  
+	RBShell *shell;
+	
+	klass = RB_BUDDYMUSIC_SOURCE_CLASS (g_type_class_peek (RB_TYPE_BUDDYMUSIC_SOURCE));
+	parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+	
+	source = RB_BUDDYMUSIC_SOURCE (parent_class->constructor (type, n_construct_properties,
+				construct_properties));
+	priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	
+	g_object_get (source, "shell", &shell, NULL);
+	g_object_get (shell, "db", &priv->db, NULL);
+	g_object_unref (shell);
+
+	/* set up buddy list view */
+	priv->buddies = rb_entry_view_new (priv->db, NULL, FALSE, FALSE);
+	
+	/* The columns we need */
+	/* A custom one for the buddy icon */
+	{
+		GtkTreeViewColumn *column = gtk_tree_view_column_new ();
+		GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new ();
+		gtk_tree_view_column_pack_start (column, renderer, TRUE);
+		
+		gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
+		gtk_tree_view_column_set_fixed_width (column, 24);
+		
+		gtk_tree_view_column_set_clickable (column, TRUE);
+		gtk_tree_view_column_set_cell_data_func (column,
+				renderer,
+				(GtkTreeCellDataFunc) rb_buddymusic_source_get_icon_cell_data,
+				source,
+				NULL);
+		rb_entry_view_append_column_custom (priv->buddies,
+				column, 
+				"",
+				"BuddyIcon",
+				NULL,
+				NULL);
+	}
+	/* A custom one for the buddy alias, stored in prop_genre */
+	{
+		GtkTreeViewColumn *column = gtk_tree_view_column_new ();
+		GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
+		gtk_tree_view_column_pack_start (column, renderer, TRUE);
+		gtk_tree_view_column_set_expand (column, TRUE);
+		gtk_tree_view_column_set_clickable (column, TRUE);
+		gtk_tree_view_column_set_cell_data_func (column,
+				renderer,
+				(GtkTreeCellDataFunc) rb_buddymusic_source_get_alias_cell_data,
+				source,
+				NULL);
+		rb_entry_view_append_column_custom (priv->buddies,
+				column, 
+				"Buddy",
+				"BuddyAlias",
+				NULL,
+				NULL);
+	}
+	rb_entry_view_append_column (priv->buddies, RB_ENTRY_VIEW_COL_ARTIST);
+	rb_entry_view_append_column (priv->buddies, RB_ENTRY_VIEW_COL_ALBUM);
+	rb_entry_view_append_column (priv->buddies, RB_ENTRY_VIEW_COL_TITLE);
+	rb_entry_view_append_column (priv->buddies, RB_ENTRY_VIEW_COL_LAST_PLAYED);
+	
+	/* Show the list */
+	gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (priv->buddies));
+	gtk_widget_show_all (GTK_WIDGET (source));
+	
+	/* Populate the buddy list */
+	rb_buddymusic_source_do_query (source);
+	
+	/* Emit need buddy signal so we start receiving the existing buddies */
+	g_signal_emit_by_name (source, "need-buddies");
+	
+	return G_OBJECT (source);
+}
+
+static void
+rb_buddymusic_source_finalize (GObject *object)
+{
+	RBBuddyMusicSource *source;
+	RBBuddyMusicSourcePrivate *priv;
+	
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (RB_IS_BUDDYMUSIC_SOURCE (object));
+
+	source = RB_BUDDYMUSIC_SOURCE (object);
+	priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	
+	rb_debug ("finalizing buddymusic source");
+
+	/* Free stuff here */
+	g_hash_table_destroy (priv->buddyuris);
+	g_hash_table_destroy (priv->buddyicons);
+	g_object_unref (priv->db);
+	
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+RBSource *
+rb_buddymusic_source_new (RBShell *shell)
+{
+	RBSource *source;
+	
+	rb_debug ("init buddymusic source");
+	source = RB_SOURCE (g_object_new (RB_TYPE_BUDDYMUSIC_SOURCE,
+					  "name", _("Buddy Music"),
+					  "shell", shell,
+					  "visibility", TRUE,
+					  NULL));
+
+	rb_shell_register_entry_type_for_source (shell, source, 
+						 RHYTHMDB_ENTRY_TYPE_BUDDYMUSIC);
+	
+	/*
+	rb_buddymusic_source_add_buddy (RB_BUDDYMUSIC_SOURCE (source),
+								"name@network.com",
+								"My buddy alias",
+								"/home/kikidonk/.gaim/icons/44709a87",
+								"http://127.0.0.1:10333",
+								&error);
+	
+	rb_buddymusic_source_update_buddy (RB_BUDDYMUSIC_SOURCE (source),
+								"name@network.com",
+								"Some artist",
+								"Album name",
+								"My own, precious, title",
+								&error);			
+	*/
+	return source;
+}
+
+static void
+rb_buddymusic_source_get_icon_cell_data(GtkTreeViewColumn *column,
+										GtkCellRenderer *renderer,
+										GtkTreeModel *tree_model,
+										GtkTreeIter *iter,
+										RBBuddyMusicSource *source)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RhythmDBEntry *entry;
+	const gchar *location;
+	GdkPixbuf *icon;
+	GError *error = NULL;
+	
+	/* Get the favicon filename */
+	gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
+	location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT);
+	if (location == NULL || *location == '\0')
+	{
+		g_object_set (G_OBJECT (renderer), "pixbuf", NULL, NULL);
+		return;
+	}
+	
+	icon = g_hash_table_lookup (priv->buddyicons, location);
+	if (icon == NULL)
+	{
+		rb_debug ("Requesting icon for alias: %s, loc: %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT), location);
+		icon = gdk_pixbuf_new_from_file_at_size (location, 24, 24, &error);
+		if (icon == NULL)
+		{
+			g_object_set (G_OBJECT (renderer), "pixbuf", NULL, NULL);
+			rb_debug ("Error while creating buddy icon: %s", error->message);
+			g_error_free (error);
+			return;
+		}
+		g_hash_table_insert (priv->buddyicons, g_strdup (location), icon);
+	}
+	
+	g_object_set (G_OBJECT (renderer), "pixbuf", icon, NULL);
+}
+
+static void
+rb_buddymusic_source_get_alias_cell_data(GtkTreeViewColumn *column,
+										GtkCellRenderer *renderer,
+										GtkTreeModel *tree_model,
+										GtkTreeIter *iter,
+										RBBuddyMusicSource *source)
+{
+	RhythmDBEntry *entry;
+	const gchar *alias;
+	
+	gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
+	alias = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE);
+
+	g_object_set (G_OBJECT (renderer), "text", alias, NULL);
+}
+
+static RBEntryView *
+rb_buddymusic_source_get_entry_view (RBSource *asource)
+{
+	return RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (asource)->buddies;
+}
+
+static char *
+rb_buddymusic_source_get_status (RBSource *asource)
+{
+ 	RBBuddyMusicSource *source = RB_BUDDYMUSIC_SOURCE (asource);
+
+	guint num_entries = rb_entry_view_get_num_entries (RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source)->buddies);
+	
+	return g_strdup_printf ("%d buddies streaming music", num_entries);
+}
+
+static GdkPixbuf *
+rb_buddymusic_source_get_pixbuf (RBSource *asource)
+{
+	return RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (asource)->icon;
+}
+
+static void
+rb_buddymusic_source_do_query (RBBuddyMusicSource *source)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RhythmDBQueryModel *query_model;
+	GPtrArray *query;
+	
+	/* The empty query model */
+	query_model = rhythmdb_query_model_new_empty (priv->db);
+	
+	/* We want all entries of our TYPE */
+	query = rhythmdb_query_parse (priv->db,
+						RHYTHMDB_QUERY_PROP_EQUALS,
+							RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_BUDDYMUSIC,
+						RHYTHMDB_QUERY_END);
+	
+	/* Go on, search, and create the tree model */	
+	rhythmdb_do_full_query_parsed (priv->db,
+						GTK_TREE_MODEL (query_model),
+						query);
+
+	/* Update the entryview with our new model,
+	   additions will be automagically reflected in the view */
+	rb_entry_view_set_model (priv->buddies, query_model);
+
+	/* Cleanup */			
+	rhythmdb_query_free (query);
+	g_object_unref (query_model);
+}
+
+	
+gboolean
+rb_buddymusic_source_add_buddy (RBBuddyMusicSource *source,
+								gchar *name,
+								gchar *alias,
+								gchar *icon,
+								gchar *url,
+								GError **error)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RhythmDBEntry *entry;
+	GValue val = { 0, };
+	
+	/* FIXME: this sucks, for now.
+	Buddy Alias stored in RHYTHMDB_PROP_GENRE
+	Buddy Icon stored in RHYTHMDB_PROP_MOUNTPOINT
+	Title, artist, album, in their respective values
+	*/
+	
+	rb_debug ("adding buddy, %s|%s|%s|%s", name, alias, icon, url);
+	
+	/* First remove anyone that has the same name, or we create dups */
+	rb_buddymusic_source_remove_buddy (source, name, NULL);
+	
+	/* Remember the buddy url, given the buddy address */
+	g_hash_table_insert (priv->buddyuris, g_strdup (name), g_strdup (url));
+
+	/* Create the new database entry for this buddy */
+	entry = rhythmdb_entry_new (priv->db, RHYTHMDB_ENTRY_TYPE_BUDDYMUSIC, url);
+	
+	/* FIXME: see above */
+	g_value_init (&val, G_TYPE_STRING);
+	g_value_set_string (&val, alias);
+	rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_GENRE, &val);
+	g_value_unset (&val);
+
+	g_value_init (&val, G_TYPE_STRING);
+	g_value_set_string (&val, icon);
+	rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_MOUNTPOINT, &val);
+	g_value_unset (&val);
+	
+	/* Update the database */	
+	rhythmdb_commit (priv->db);
+
+	return TRUE;
+}
+
+gboolean
+rb_buddymusic_source_update_buddy (RBBuddyMusicSource *source,
+									gchar *name,
+									gchar *artist,
+									gchar *album,
+									gchar *title,
+									GError **error)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RhythmDBEntry *entry;
+	GValue val = { 0, };
+	gchar *url;
+	
+	rb_debug ("trying update buddy, %s|%s|%s|%s", name, artist, album, title);
+	
+	url = g_hash_table_lookup (priv->buddyuris, name);
+	rb_debug ("trying update: got url: %s", url);
+	if (url == NULL)
+	{
+		/* Noone has this name, ignoring */
+		return TRUE;
+	}
+	
+	entry = rhythmdb_entry_lookup_by_location (priv->db, url);
+	rb_debug ("trying update: got entry: %p", entry);
+	if (entry == NULL)
+	{
+		/* Noone has that name, ignoring */
+		return TRUE;
+	}
+	
+	rb_debug ("update buddy, %s|%s|%s|%s", name, artist, album, title);
+	
+	g_value_init (&val, G_TYPE_STRING);
+	g_value_set_string (&val, artist);
+	rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_ARTIST, &val);
+	g_value_unset (&val);
+
+	g_value_init (&val, G_TYPE_STRING);
+	g_value_set_string (&val, album);
+	rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_ALBUM, &val);
+	g_value_unset (&val);
+	
+	g_value_init (&val, G_TYPE_STRING);
+	g_value_set_string (&val, title);
+	rhythmdb_entry_set (priv->db, entry, RHYTHMDB_PROP_TITLE, &val);
+	g_value_unset (&val);
+	
+	rhythmdb_commit (priv->db);
+	
+	return TRUE;
+}
+
+gboolean
+rb_buddymusic_source_remove_buddy (RBBuddyMusicSource *source,
+									gchar *name,
+									GError **error)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RhythmDBEntry *entry;
+	gchar *url;
+	const gchar *favicon;
+	
+	url = g_hash_table_lookup (priv->buddyuris, name);
+	if (url == NULL)
+	{
+		/* Noone has this name, ignoring */
+		return TRUE;
+	}
+	entry = rhythmdb_entry_lookup_by_location (priv->db, url);
+	if (entry == NULL)
+	{
+		/* Noone has this name, ignoring */
+		return TRUE;
+	}
+	
+	/* Remove it from the DB */
+	g_hash_table_remove (priv->buddyuris, name);
+	favicon = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MOUNTPOINT);
+	if (favicon != NULL)
+	{
+		g_hash_table_remove (priv->buddyicons, favicon);
+	}
+	rhythmdb_entry_delete (priv->db, entry);
+	
+	/* Update the database */	
+	rhythmdb_commit (priv->db);
+	
+	return TRUE;
+}
+
+gboolean
+rb_buddymusic_source_play_stream (RBBuddyMusicSource *source,
+									gchar *name,
+									GError **error)
+{
+	RBBuddyMusicSourcePrivate *priv = RB_BUDDYMUSIC_SOURCE_GET_PRIVATE (source);
+	RBShell *shell;
+	RBRemoteProxy *proxy;
+	gchar *url;
+	gchar *playing;
+	
+	url = g_hash_table_lookup (priv->buddyuris, name);
+	if (url == NULL)
+	{
+		/* FIXME: is that ok ? Noone has this name, ignoring */
+		return TRUE;
+	}
+	
+	/* Get the remote control */
+	g_object_get (source, "shell", &shell, NULL);
+	proxy = RB_REMOTE_PROXY (shell);
+	
+	/* If the currently playing song isn't this one, then switch, otherwise
+	  let it continue without interruption */
+	playing = rb_remote_proxy_get_playing_uri (proxy);
+	rb_debug ("Currently playing: %s, want to play: %s", playing, url);
+	if (playing == NULL || strcmp (playing, url) != 0)
+	{
+		rb_debug ("Switching to new feed");
+		rb_remote_proxy_play_uri (proxy, url);	
+	}
+
+	/* Grab the focus ! */
+	rb_remote_proxy_select_uri (proxy, url);
+	rb_remote_proxy_grab_focus (proxy);
+	
+	/* Cleanup */
+	g_free (playing);
+	g_object_unref (shell);
+	
+	return TRUE;
+}
Index: sources/rb-buddymusic-source.h
===================================================================
RCS file: sources/rb-buddymusic-source.h
diff -N sources/rb-buddymusic-source.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sources/rb-buddymusic-source.h	31 Aug 2005 11:25:24 -0000
@@ -0,0 +1,88 @@
+/* 
+ *  For use with gShrooms, provide a buddy music source to rhythmbox
+ *
+ *  Copyright (C) 2005 Raphael Slinckx <raphael@slinckx.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_BUDDYMUSIC_SOURCE_H
+#define __RB_BUDDYMUSIC_SOURCE_H
+
+#include <dbus/dbus-glib.h>
+#include "rb-shell.h"
+#include "rb-source.h"
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_BUDDYMUSIC_SOURCE         (rb_buddymusic_source_get_type ())
+#define RB_BUDDYMUSIC_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_BUDDYMUSIC_SOURCE, RBBuddyMusicSource))
+#define RB_BUDDYMUSIC_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_BUDDYMUSIC_SOURCE, RBBuddyMusicSourceClass))
+#define RB_IS_BUDDYMUSIC_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_BUDDYMUSIC_SOURCE))
+#define RB_IS_BUDDYMUSIC_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_BUDDYMUSIC_SOURCE))
+#define RB_BUDDYMUSIC_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_BUDDYMUSIC_SOURCE, RBBuddyMusicSourceClass))
+
+typedef struct _RBBuddyMusicSource 			RBBuddyMusicSource;
+typedef struct _RBBuddyMusicSourceClass		RBBuddyMusicSourceClass;
+typedef struct _RBBuddyMusicSourcePrivate 	RBBuddyMusicSourcePrivate;
+
+struct _RBBuddyMusicSource
+{
+	RBSource parent;
+
+	RBBuddyMusicSourcePrivate *priv;
+};
+
+struct _RBBuddyMusicSourceClass
+{
+	RBSourceClass parent;
+	
+	DBusGConnection *connection;
+	
+	/* signals */
+	void (* need_buddies) (RBBuddyMusicSource *self);
+};
+
+GType		rb_buddymusic_source_get_type	(void);
+
+RBSource *	rb_buddymusic_source_new		(RBShell *shell);
+
+gboolean rb_buddymusic_source_add_buddy (RBBuddyMusicSource *source,
+										gchar *name,
+										gchar *alias,
+										gchar *icon,
+										gchar *url,
+										GError **error);
+
+gboolean rb_buddymusic_source_update_buddy (RBBuddyMusicSource *source,
+										gchar *name,
+										gchar *artist,
+										gchar *album,
+										gchar *title,
+										GError **error);
+
+gboolean rb_buddymusic_source_remove_buddy (RBBuddyMusicSource *source,
+										gchar *name,
+										GError **error);
+
+gboolean rb_buddymusic_source_play_stream (RBBuddyMusicSource *source,
+										gchar *name,
+										GError **error);
+									
+G_END_DECLS
+
+#endif /* __RB_BUDDYMUSIC_SOURCE_H */
Index: sources/rb-buddymusic-source.xml
===================================================================
RCS file: sources/rb-buddymusic-source.xml
diff -N sources/rb-buddymusic-source.xml
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sources/rb-buddymusic-source.xml	31 Aug 2005 11:25:24 -0000
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/gnome/Rhythmbox/BuddyMusic">
+	<interface name="org.gnome.Rhythmbox.BuddyMusic">
+		<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="rb_buddymusic_source"/>
+		<!-- METHODS -->
+		<method name="AddBuddy">
+			<arg type="s" name="name" direction="in" />
+			<arg type="s" name="alias" direction="in" />
+			<arg type="s" name="icon" direction="in" />
+			<arg type="s" name="url" direction="in" />
+		</method>
+		<method name="UpdateBuddy">
+			<arg type="s" name="name" direction="in" />
+			<arg type="s" name="artist" direction="in" />
+			<arg type="s" name="album" direction="in" />
+			<arg type="s" name="title" direction="in" />
+		</method>
+		<method name="RemoveBuddy">
+			<arg type="s" name="name" direction="in" />
+		</method>		
+		<method name="PlayStream">
+			<arg type="s" name="name" direction="in" />
+		</method>
+		
+		<!-- SIGNALS -->
+		<signal name="NeedBuddies" />
+	</interface>
+</node>
