diff -u -u -r1.46 Makefile.am
--- Makefile.am	19 Feb 2006 16:55:59 -0000	1.46
+++ Makefile.am	26 Feb 2006 13:35:44 -0000
@@ -5,7 +5,7 @@
 DISTCHECK_CONFIGURE_FLAGS = --disable-schemas-install --enable-gtk-doc --disable-scrollkeeper
 distuninstallcheck_listfiles = find . -type f -print | grep -v '^\./var/scrollkeeper'
 
-SUBDIRS = macros lib metadata player rhythmdb widgets sources iradio podcast remote daapsharing shell data po help tests doc
+SUBDIRS = macros lib metadata player rhythmdb widgets sources iradio podcast remote daapsharing backends shell data po help tests doc
 
 EXTRA_DIST = 			\
 	autogen.sh		\
diff -u -u -r1.212 configure.ac
--- configure.ac	22 Feb 2006 20:05:46 -0000	1.212
+++ configure.ac	26 Feb 2006 13:35:45 -0000
@@ -713,6 +717,8 @@
 tests/Makefile
 doc/Makefile
 doc/reference/Makefile
+backends/Makefile
+backends/gstreamer/Makefile
 ])
 
 AC_MSG_NOTICE([Rhythmbox was configured with the following options:])
diff -u -u -r1.12 general-prefs.glade
--- data/glade/general-prefs.glade	18 Feb 2006 06:56:17 -0000	1.12
+++ data/glade/general-prefs.glade	26 Feb 2006 13:35:45 -0000
@@ -28,6 +28,190 @@
       <property name="spacing">18</property>
 
       <child>
+	<widget class="GtkVBox" id="vbox14">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label22">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">Browser Views</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkHBox" id="hbox16">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label23">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes"></property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox17">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="browser_views_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes"></property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">8</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkVBox" id="vbox15">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">6</property>
+
+		      <child>
+			<widget class="GtkRadioButton" id="library_browser_views_radio">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">_Artists and albums</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <property name="active">True</property>
+			  <property name="inconsistent">False</property>
+			  <property name="draw_indicator">True</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkRadioButton" id="library_browser_views_radio">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">_Genres and artists</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <property name="active">False</property>
+			  <property name="inconsistent">False</property>
+			  <property name="draw_indicator">True</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkRadioButton" id="library_browser_views_radio">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="label" translatable="yes">G_enres, artists and albums</property>
+			  <property name="use_underline">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <property name="active">False</property>
+			  <property name="inconsistent">False</property>
+			  <property name="draw_indicator">True</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
 	<widget class="GtkVBox" id="vbox2">
 	  <property name="visible">True</property>
 	  <property name="homogeneous">False</property>
diff -u -u -r1.10 library-prefs.glade
--- data/glade/library-prefs.glade	23 Feb 2006 11:43:37 -0000	1.10
+++ data/glade/library-prefs.glade	26 Feb 2006 13:35:45 -0000
@@ -40,195 +40,6 @@
 	  <property name="spacing">18</property>
 
 	  <child>
-	    <widget class="GtkVBox" id="vbox9">
-	      <property name="visible">True</property>
-	      <property name="homogeneous">False</property>
-	      <property name="spacing">6</property>
-
-	      <child>
-		<widget class="GtkLabel" id="browser_views_label">
-		  <property name="visible">True</property>
-		  <property name="label" translatable="yes">Browser Views</property>
-		  <property name="use_underline">False</property>
-		  <property name="use_markup">False</property>
-		  <property name="justify">GTK_JUSTIFY_LEFT</property>
-		  <property name="wrap">False</property>
-		  <property name="selectable">False</property>
-		  <property name="xalign">0</property>
-		  <property name="yalign">0.5</property>
-		  <property name="xpad">0</property>
-		  <property name="ypad">0</property>
-		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-		  <property name="width_chars">-1</property>
-		  <property name="single_line_mode">False</property>
-		  <property name="angle">0</property>
-		</widget>
-		<packing>
-		  <property name="padding">0</property>
-		  <property name="expand">False</property>
-		  <property name="fill">False</property>
-		</packing>
-	      </child>
-
-	      <child>
-		<widget class="GtkHBox" id="hbox12">
-		  <property name="visible">True</property>
-		  <property name="homogeneous">False</property>
-		  <property name="spacing">0</property>
-
-		  <child>
-		    <widget class="GtkLabel" id="label16">
-		      <property name="visible">True</property>
-		      <property name="label" translatable="yes"></property>
-		      <property name="use_underline">False</property>
-		      <property name="use_markup">False</property>
-		      <property name="justify">GTK_JUSTIFY_LEFT</property>
-		      <property name="wrap">False</property>
-		      <property name="selectable">False</property>
-		      <property name="xalign">0.5</property>
-		      <property name="yalign">0.5</property>
-		      <property name="xpad">0</property>
-		      <property name="ypad">0</property>
-		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-		      <property name="width_chars">-1</property>
-		      <property name="single_line_mode">False</property>
-		      <property name="angle">0</property>
-		    </widget>
-		    <packing>
-		      <property name="padding">0</property>
-		      <property name="expand">False</property>
-		      <property name="fill">False</property>
-		    </packing>
-		  </child>
-
-		  <child>
-		    <widget class="GtkHBox" id="hbox13">
-		      <property name="visible">True</property>
-		      <property name="homogeneous">False</property>
-		      <property name="spacing">0</property>
-
-		      <child>
-			<widget class="GtkLabel" id="label17">
-			  <property name="visible">True</property>
-			  <property name="label" translatable="yes"></property>
-			  <property name="use_underline">False</property>
-			  <property name="use_markup">False</property>
-			  <property name="justify">GTK_JUSTIFY_LEFT</property>
-			  <property name="wrap">False</property>
-			  <property name="selectable">False</property>
-			  <property name="xalign">0.5</property>
-			  <property name="yalign">0.5</property>
-			  <property name="xpad">8</property>
-			  <property name="ypad">0</property>
-			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-			  <property name="width_chars">-1</property>
-			  <property name="single_line_mode">False</property>
-			  <property name="angle">0</property>
-			</widget>
-			<packing>
-			  <property name="padding">0</property>
-			  <property name="expand">False</property>
-			  <property name="fill">False</property>
-			</packing>
-		      </child>
-
-		      <child>
-			<widget class="GtkVBox" id="vbox10">
-			  <property name="visible">True</property>
-			  <property name="homogeneous">False</property>
-			  <property name="spacing">6</property>
-
-			  <child>
-			    <widget class="GtkRadioButton" id="library_browser_views_radio">
-			      <property name="visible">True</property>
-			      <property name="can_focus">True</property>
-			      <property name="label" translatable="yes">_Artists and albums</property>
-			      <property name="use_underline">True</property>
-			      <property name="relief">GTK_RELIEF_NORMAL</property>
-			      <property name="focus_on_click">True</property>
-			      <property name="active">True</property>
-			      <property name="inconsistent">False</property>
-			      <property name="draw_indicator">True</property>
-			      <signal name="toggled" handler="rb_library_source_browser_views_activated_cb" last_modification_time="Sun, 26 Jan 2003 20:03:41 GMT"/>
-			    </widget>
-			    <packing>
-			      <property name="padding">0</property>
-			      <property name="expand">False</property>
-			      <property name="fill">False</property>
-			    </packing>
-			  </child>
-
-			  <child>
-			    <widget class="GtkRadioButton" id="radiobutton2">
-			      <property name="visible">True</property>
-			      <property name="can_focus">True</property>
-			      <property name="label" translatable="yes">_Genres and artists</property>
-			      <property name="use_underline">True</property>
-			      <property name="relief">GTK_RELIEF_NORMAL</property>
-			      <property name="focus_on_click">True</property>
-			      <property name="active">False</property>
-			      <property name="inconsistent">False</property>
-			      <property name="draw_indicator">True</property>
-			      <property name="group">library_browser_views_radio</property>
-			      <signal name="toggled" handler="rb_library_source_browser_views_activated_cb" last_modification_time="Sun, 26 Jan 2003 20:03:48 GMT"/>
-			    </widget>
-			    <packing>
-			      <property name="padding">0</property>
-			      <property name="expand">False</property>
-			      <property name="fill">False</property>
-			    </packing>
-			  </child>
-
-			  <child>
-			    <widget class="GtkRadioButton" id="radiobutton3">
-			      <property name="visible">True</property>
-			      <property name="can_focus">True</property>
-			      <property name="label" translatable="yes">G_enres, artists and albums</property>
-			      <property name="use_underline">True</property>
-			      <property name="relief">GTK_RELIEF_NORMAL</property>
-			      <property name="focus_on_click">True</property>
-			      <property name="active">False</property>
-			      <property name="inconsistent">False</property>
-			      <property name="draw_indicator">True</property>
-			      <property name="group">library_browser_views_radio</property>
-			      <signal name="toggled" handler="rb_library_source_browser_views_activated_cb" last_modification_time="Sun, 26 Jan 2003 20:03:56 GMT"/>
-			    </widget>
-			    <packing>
-			      <property name="padding">0</property>
-			      <property name="expand">False</property>
-			      <property name="fill">False</property>
-			    </packing>
-			  </child>
-			</widget>
-			<packing>
-			  <property name="padding">0</property>
-			  <property name="expand">True</property>
-			  <property name="fill">True</property>
-			</packing>
-		      </child>
-		    </widget>
-		    <packing>
-		      <property name="padding">0</property>
-		      <property name="expand">True</property>
-		      <property name="fill">True</property>
-		    </packing>
-		  </child>
-		</widget>
-		<packing>
-		  <property name="padding">0</property>
-		  <property name="expand">True</property>
-		  <property name="fill">True</property>
-		</packing>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="padding">0</property>
-	      <property name="expand">False</property>
-	      <property name="fill">False</property>
-	    </packing>
-	  </child>
-
-	  <child>
 	    <widget class="GtkVBox" id="vbox11">
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
@@ -466,32 +277,295 @@
 	    </widget>
 	    <packing>
 	      <property name="padding">0</property>
-	      <property name="expand">True</property>
-	      <property name="fill">True</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
 	    </packing>
 	  </child>
 
 	  <child>
-	    <widget class="GtkAlignment" id="alignment2">
+	    <widget class="GtkVBox" id="vbox13">
 	      <property name="visible">True</property>
-	      <property name="xalign">0.5</property>
-	      <property name="yalign">0.5</property>
-	      <property name="xscale">1</property>
-	      <property name="yscale">1</property>
-	      <property name="top_padding">0</property>
-	      <property name="bottom_padding">0</property>
-	      <property name="left_padding">0</property>
-	      <property name="right_padding">0</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
 
 	      <child>
-		<placeholder/>
+		<widget class="GtkLabel" id="label22">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">Library Structure</property>
+		  <property name="use_underline">True</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkHBox" id="hbox17">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label23">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">    </property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkTable" id="table1">
+		      <property name="visible">True</property>
+		      <property name="n_rows">5</property>
+		      <property name="n_columns">2</property>
+		      <property name="homogeneous">False</property>
+		      <property name="row_spacing">3</property>
+		      <property name="column_spacing">6</property>
+
+		      <child>
+			<widget class="GtkLabel" id="label24">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Folder _heirarchy:</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">0</property>
+			  <property name="right_attach">1</property>
+			  <property name="top_attach">0</property>
+			  <property name="bottom_attach">1</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options"></property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label25">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">_File name:</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">0</property>
+			  <property name="right_attach">1</property>
+			  <property name="top_attach">1</property>
+			  <property name="bottom_attach">2</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options"></property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label26">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Example path:</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">0</property>
+			  <property name="right_attach">1</property>
+			  <property name="top_attach">2</property>
+			  <property name="bottom_attach">3</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options"></property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label27">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">Preferred Format:</property>
+			  <property name="use_underline">True</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">0</property>
+			  <property name="right_attach">1</property>
+			  <property name="top_attach">4</property>
+			  <property name="bottom_attach">5</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options"></property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkComboBox" id="combobox1">
+			  <property name="visible">True</property>
+			  <property name="items" translatable="yes">Artist/Artist - Album/</property>
+			  <property name="add_tearoffs">False</property>
+			  <property name="focus_on_click">True</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">1</property>
+			  <property name="right_attach">2</property>
+			  <property name="top_attach">0</property>
+			  <property name="bottom_attach">1</property>
+			  <property name="y_options">fill</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkComboBox" id="combobox2">
+			  <property name="visible">True</property>
+			  <property name="items" translatable="yes">Artist (Album) - Track Number - Title.ogg</property>
+			  <property name="add_tearoffs">False</property>
+			  <property name="focus_on_click">True</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">1</property>
+			  <property name="right_attach">2</property>
+			  <property name="top_attach">1</property>
+			  <property name="bottom_attach">2</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options">fill</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkComboBox" id="combobox3">
+			  <property name="visible">True</property>
+			  <property name="items" translatable="yes">Ogg Vorbis</property>
+			  <property name="add_tearoffs">False</property>
+			  <property name="focus_on_click">True</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">1</property>
+			  <property name="right_attach">2</property>
+			  <property name="top_attach">4</property>
+			  <property name="bottom_attach">5</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options">fill</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="label28">
+			  <property name="visible">True</property>
+			  <property name="label" translatable="yes">/path/to/music/Artist/Artist - Album/Artist (Album) - 01 - Title.ogg</property>
+			  <property name="use_underline">False</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			  <property name="width_chars">-1</property>
+			  <property name="single_line_mode">False</property>
+			  <property name="angle">0</property>
+			</widget>
+			<packing>
+			  <property name="left_attach">1</property>
+			  <property name="right_attach">2</property>
+			  <property name="top_attach">2</property>
+			  <property name="bottom_attach">3</property>
+			  <property name="x_options">fill</property>
+			  <property name="y_options"></property>
+			</packing>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
 	      </child>
 	    </widget>
 	    <packing>
 	      <property name="padding">0</property>
-	      <property name="expand">False</property>
-	      <property name="fill">False</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
 	    </packing>
+	  </child>
+
+	  <child>
+	    <placeholder/>
 	  </child>
 	</widget>
       </child>
diff -u -u -r1.4 rb-glade-helpers.c
--- lib/rb-glade-helpers.c	30 Nov 2005 07:18:29 -0000	1.4
+++ lib/rb-glade-helpers.c	26 Feb 2006 13:35:46 -0000
@@ -114,6 +114,12 @@
 rb_glade_boldify_label (GladeXML *xml, const char *name)
 {
 	GtkLabel *label = GTK_LABEL (glade_xml_get_widget (xml, name));
+
+	if (label == NULL) {
+		g_warning ("widget '%s' not found", name);
+		return;
+	}
+
 	/* this way is probably better, but for some reason doesn't work with
 	 * labels with mnemonics.
 
diff -u -u -r1.27 rb-preferences.h
--- lib/rb-preferences.h	23 Feb 2006 12:21:02 -0000	1.27
+++ lib/rb-preferences.h	26 Feb 2006 13:35:46 -0000
@@ -29,8 +29,6 @@
 
 #define CONF_FIRST_TIME CONF_PREFIX   "/first_time_flag"
 #define CONF_GRACE_PERIOD CONF_PREFIX "/grace_period"
-#define CONF_LIBRARY_LOCATION	CONF_PREFIX "/library_locations"
-#define CONF_MONITOR_LIBRARY	CONF_PREFIX "/monitor_library"
 #define CONF_UI_DIR               CONF_PREFIX "/ui"
 #define CONF_UI_STATUSBAR_HIDDEN  CONF_PREFIX "/ui/statusbar_hidden"
 #define CONF_UI_TOOLBAR_HIDDEN  CONF_PREFIX "/ui/toolbar_hidden"
@@ -57,6 +55,13 @@
 #define CONF_DAAP_SHARE_NAME       CONF_PREFIX "/sharing/share_name"
 #define CONF_DAAP_SHARE_PASSWORD   CONF_PREFIX "/sharing/share_password"
 #define CONF_DAAP_REQUIRE_PASSWORD CONF_PREFIX "/sharing/require_password"
+
+#define CONF_LIBRARY_LOCATION	CONF_PREFIX "/library_locations"
+#define CONF_MONITOR_LIBRARY	CONF_PREFIX "/monitor_library"
+#define CONF_LIBRARY_STRIP_CHARS	CONF_PREFIX "/library_strip_chars"
+#define CONF_LIBRARY_LAYOUT_PATH	CONF_PREFIX "/library_layout_path"
+#define CONF_LIBRARY_LAYOUT_FILENAME	CONF_PREFIX "/library_layout_filename"
+#define CONF_LIBRARY_PREFERRED_FORMAT	CONF_PREFIX "/library_preferred_format"
 
 G_END_DECLS
 
diff -u -u -r1.19 rb-util.c
--- lib/rb-util.c	7 Feb 2006 13:19:56 -0000	1.19
+++ lib/rb-util.c	26 Feb 2006 13:35:46 -0000
@@ -667,3 +667,51 @@
 
 	return list;
 }
+
+/*
+ * hacked up version of gnome_vfs_uri_list_parse,
+ * that it doesn't strip #s and and returns strings. 
+ */
+
+GList *
+rb_uri_list_parse (const char *uri_list)
+{
+	const gchar *p, *q;
+	gchar *retval;
+	GList *result = NULL;
+
+	g_return_val_if_fail (uri_list != NULL, NULL);
+
+	p = uri_list;
+
+	while (p != NULL) {
+		while (g_ascii_isspace (*p))
+			p++;
+
+		q = p;
+		while ((*q != '\0')
+		       && (*q != '\n')
+		       && (*q != '\r'))
+			q++;
+
+		if (q > p) {
+			q--;
+			while (q > p
+			       && g_ascii_isspace (*q))
+				q--;
+
+			retval = g_malloc (q - p + 2);
+			strncpy (retval, p, q - p + 1);
+			retval[q - p + 1] = '\0';
+
+			if (retval != NULL)
+				result = g_list_prepend (result, retval);
+		}
+		p = strchr (p, '\n');
+		if (p != NULL)
+			p++;
+	}
+
+	return g_list_reverse (result);
+}
+
diff -u -u -r1.15 rb-util.h
--- lib/rb-util.h	7 Feb 2006 13:19:56 -0000	1.15
+++ lib/rb-util.h	26 Feb 2006 13:35:46 -0000
@@ -59,6 +59,8 @@
 GList* rb_collate_hash_table_keys (GHashTable *table);
 GList* rb_collate_hash_table_values (GHashTable *table);
 
+GList* rb_uri_list_parse (const char *uri_list);
+
 G_END_DECLS
 
 #endif /* __RB_UTIL_H */
diff -u -u -r1.33 rb-podcast-manager.c
--- podcast/rb-podcast-manager.c	18 Feb 2006 09:47:12 -0000	1.33
+++ podcast/rb-podcast-manager.c	26 Feb 2006 13:35:47 -0000
@@ -143,8 +143,6 @@
 		                                	 GValue *value,
                 		                	 GParamSpec *pspec);
 static void rb_podcast_manager_copy_post 		(RBPodcastManager *pd);
-static int rb_podcast_manager_mkdir_with_parents 	(const gchar *pathname,
-					  		 int mode);
 static gboolean rb_podcast_manager_sync_head_cb 	(gpointer data);
 static gboolean rb_podcast_manager_head_query_cb 	(GtkTreeModel *query_model,
 						   	 GtkTreePath *path, 
@@ -598,7 +596,7 @@
 	g_free (conf_dir_name);
 
 	 if (!g_file_test (dir_name, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
-		if (rb_podcast_manager_mkdir_with_parents (dir_name, 0750) != 0) {
+		if (!g_mkdir_with_parents (dir_name, 0750)) {
 			rb_debug ("Error downloading podcast: could not create local dirs");
 			goto next_step;
 		}
@@ -690,62 +688,6 @@
 
 	g_mutex_unlock (pd->priv->mutex_job);
 	gtk_idle_add ((GtkFunction) rb_podcast_manager_next_file , pd);	
-}
-
-
-static int
-rb_podcast_manager_mkdir_with_parents (const gchar *pathname,
-		   			int          mode)
-{
-  gchar *fn, *p;
-
-  if (pathname == NULL || *pathname == '\0')
-    {
-      return -1;
-    }
-
-  fn = g_strdup (pathname);
-
-  if (g_path_is_absolute (fn))
-    p = (gchar *) g_path_skip_root (fn);
-  else
-    p = fn;
-
-  do
-    {
-      while (*p && !G_IS_DIR_SEPARATOR (*p))
-	p++;
-      
-      if (!*p)
-	p = NULL;
-      else
-	*p = '\0';
-      
-      if (!g_file_test (fn, G_FILE_TEST_EXISTS))
-	{
-	  if (g_mkdir (fn, mode) == -1)
-	    {
-	      g_free (fn);
-	      return -1;
-	    }
-	}
-      else if (!g_file_test (fn, G_FILE_TEST_IS_DIR))
-	{
-	  g_free (fn);
-	  return -1;
-	}
-      if (p)
-	{
-	  *p++ = G_DIR_SEPARATOR;
-	  while (*p && G_IS_DIR_SEPARATOR (*p))
-	    p++;
-	}
-    }
-  while (p);
-
-  g_free (fn);
-
-  return 0;
 }
 
 gboolean
diff -u -u -r1.68 Makefile.am
--- shell/Makefile.am	7 Feb 2006 13:19:57 -0000	1.68
+++ shell/Makefile.am	26 Feb 2006 13:35:47 -0000
@@ -87,6 +87,7 @@
 	$(top_builddir)/metadata/librbmetadata.la	\
 	$(top_builddir)/widgets/librbwidgets.la         \
 	$(top_builddir)/rhythmdb/librhythmdb.la         \
+	$(top_builddir)/backends/librbbackends.la       \
 	$(top_builddir)/lib/librb.la	                \
 	$(GDA_LIBS)					\
 	$(TOTEM_PLPARSER_LIBS)				\
diff -u -u -r1.35 rb-shell-clipboard.c
--- shell/rb-shell-clipboard.c	17 Feb 2006 15:56:42 -0000	1.35
+++ shell/rb-shell-clipboard.c	26 Feb 2006 13:35:48 -0000
@@ -392,7 +392,7 @@
 	rb_debug ("syncing clipboard");
 	
 	if (g_list_length (clipboard->priv->entries) > 0)
-		can_paste = rb_source_can_cut (clipboard->priv->source);
+		can_paste = rb_source_can_paste (clipboard->priv->source);
 
 	if (have_selection) {
 		can_cut = rb_source_can_cut (clipboard->priv->source);
diff -u -u -r1.57 rb-shell-preferences.c
--- shell/rb-shell-preferences.c	18 Feb 2006 06:56:17 -0000	1.57
+++ shell/rb-shell-preferences.c	26 Feb 2006 13:35:48 -0000
@@ -82,6 +82,8 @@
 	GtkWidget *quality_check;
 	GtkWidget *year_check;
 
+	GSList *browser_views_group;
+
 	gboolean loading;
 };
 
@@ -119,7 +121,7 @@
 static void
 rb_shell_preferences_init (RBShellPreferences *shell_preferences)
 {
-	GtkWidget *help;
+	GtkWidget *tmp;
 	GladeXML *xml;
 
 	shell_preferences->priv = RB_SHELL_PREFERENCES_GET_PRIVATE (shell_preferences);
@@ -136,10 +138,10 @@
 	gtk_dialog_add_button (GTK_DIALOG (shell_preferences),
 			       GTK_STOCK_CLOSE,
 			       GTK_RESPONSE_CLOSE);
-	help = gtk_dialog_add_button (GTK_DIALOG (shell_preferences),
+	tmp = gtk_dialog_add_button (GTK_DIALOG (shell_preferences),
 			              GTK_STOCK_HELP,
 			              GTK_RESPONSE_HELP);
-	g_signal_connect_object (G_OBJECT (help), "clicked",
+	g_signal_connect_object (G_OBJECT (tmp), "clicked",
 				 G_CALLBACK (help_cb), shell_preferences, 0);
 	gtk_dialog_set_default_response (GTK_DIALOG (shell_preferences),
 					 GTK_RESPONSE_CLOSE);
@@ -187,6 +189,14 @@
 	shell_preferences->priv->first_seen_check =
 		glade_xml_get_widget (xml, "first_seen_check");
 
+	/* browser options */
+	rb_glade_boldify_label (xml, "browser_views_label");
+
+	tmp = glade_xml_get_widget (xml, "library_browser_views_radio");
+	shell_preferences->priv->browser_views_group =
+		g_slist_reverse (g_slist_copy (gtk_radio_button_get_group 
+					       (GTK_RADIO_BUTTON (tmp))));
+
 	gtk_notebook_append_page (GTK_NOTEBOOK (shell_preferences->priv->notebook),
 				  glade_xml_get_widget (xml, "general_vbox"),
 				  gtk_label_new (_("General")));
@@ -533,6 +543,7 @@
 rb_shell_preferences_sync (RBShellPreferences *shell_preferences)
 {
 	char *columns;
+	GSList  *list;
 
 	shell_preferences->priv->loading = TRUE;
 
@@ -577,6 +588,10 @@
 	}
 
 	g_free (columns);
+
+	list = g_slist_nth (shell_preferences->priv->browser_views_group,
+			    eel_gconf_get_integer (CONF_UI_BROWSER_VIEWS));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (list->data), TRUE);
 
 	shell_preferences->priv->loading = FALSE;
 }
diff -u -u -r1.33 Makefile.am
--- sources/Makefile.am	22 Feb 2006 13:06:01 -0000	1.33
+++ sources/Makefile.am	26 Feb 2006 13:35:48 -0000
@@ -54,6 +54,7 @@
 	-I$(top_srcdir)/shell				\
 	-I$(top_srcdir)/daapsharing			\
 	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/backends			\
 	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
 	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
 	-DDATADIR=\""$(datadir)"\"			\
@@ -61,10 +62,13 @@
 	$(TOTEM_PLPARSER_CFLAGS)			\
 	$(HAL_CFLAGS)					\
 	$(LIBNAUTILUS_BURN_CFLAGS)			\
+	$(LIBJUICER_CFLAGS)				\
 	$(RHYTHMBOX_CFLAGS)
 
 libsources_la_LDFLAGS = -export-dynamic
 libsourcesimpl_la_LDFLAGS = -export-dynamic
+
+libsourcesimpl_la_LIBADD = $(LIBJUICER_LIBS)
 
 if USE_IPOD
 libsourcesimpl_la_SOURCES +=         \
diff -u -u -r1.23 rb-audiocd-source.c
--- sources/rb-audiocd-source.c	23 Feb 2006 03:54:51 -0000	1.23
+++ sources/rb-audiocd-source.c	26 Feb 2006 13:35:48 -0000
@@ -92,6 +92,9 @@
 	/* don't bother showing the browser/search bits */
 	source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_can_browse = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
 
 	source_class->impl_show_popup = impl_show_popup;
 	source_class->impl_delete_thyself = impl_delete_thyself;
@@ -269,6 +272,7 @@
 	entry_set_string_prop (db, entry, FALSE, RHYTHMDB_PROP_ARTIST, NULL);
 	entry_set_string_prop (db, entry, FALSE, RHYTHMDB_PROP_ALBUM, NULL);
 	entry_set_string_prop (db, entry, FALSE, RHYTHMDB_PROP_GENRE, NULL);
+	entry_set_string_prop (db, entry, FALSE, RHYTHMDB_PROP_MIMETYPE, "audio/x-raw-int");
 
 	rhythmdb_commit (db);
 	g_free (audio_path);
diff -u -u -r1.151 rb-library-source.c
--- sources/rb-library-source.c	24 Feb 2006 02:34:40 -0000	1.151
+++ sources/rb-library-source.c	26 Feb 2006 13:35:49 -0000
@@ -48,6 +48,9 @@
 #include "rb-preferences.h"
 #include "rb-shell-preferences.h"
 
+#include <profiles/gnome-media-profiles.h>
+#include "rb-encoder.h"
+
 static void rb_library_source_class_init (RBLibrarySourceClass *klass);
 static void rb_library_source_init (RBLibrarySource *source);
 static GObject *rb_library_source_constructor (GType type, guint n_construct_properties,
@@ -106,12 +109,12 @@
 static GtkWidget *impl_get_config_widget (RBSource *source, RBShellPreferences *prefs);
 static void impl_song_properties (RBSource *source);
 static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data);
+static gboolean impl_can_paste (RBSource *asource);
+static void impl_paste (RBSource *source, GList *entries);
 static gboolean impl_show_popup (RBSource *source);
 static const char * impl_get_paned_key (RBLibrarySource *source);
 static void rb_library_source_do_query (RBLibrarySource *source);
 
-void rb_library_source_browser_views_activated_cb (GtkWidget *widget,
-						 RBLibrarySource *source);
 static GPtrArray * construct_query_from_selection (RBLibrarySource *source);
 static void songs_view_drag_data_received_cb (GtkWidget *widget,
 					      GdkDragContext *dc,
@@ -135,7 +138,6 @@
 	
 	RBLibraryBrowser *browser;
 	GtkWidget *vbox;
-	GSList *browser_views_group;
 
 	GdkPixbuf *pixbuf;
 
@@ -218,6 +220,7 @@
 	source_class->impl_song_properties = impl_song_properties;
 	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_paste = (RBSourceFeatureFunc) impl_can_paste;
 	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_true_function;
@@ -227,6 +230,7 @@
 	source_class->impl_have_url = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_receive_drag = impl_receive_drag;
 	source_class->impl_show_popup = impl_show_popup;
+	source_class->impl_paste = impl_paste;
 
 	klass->impl_get_paned_key = impl_get_paned_key;
 	klass->impl_has_first_added_column = (RBLibrarySourceFeatureFunc) rb_true_function;
@@ -251,6 +255,8 @@
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	g_type_class_add_private (klass, sizeof (RBLibrarySourcePrivate));
+
+	gnome_media_profiles_init (gconf_client_get_default ());
 }
 
 static void
@@ -699,10 +705,6 @@
 	xml = rb_glade_xml_new ("library-prefs.glade", "library_vbox", source);
 	source->priv->config_widget =
 		glade_xml_get_widget (xml, "library_vbox");
-	tmp = glade_xml_get_widget (xml, "library_browser_views_radio");
-	source->priv->browser_views_group =
-		g_slist_reverse (g_slist_copy (gtk_radio_button_get_group 
-					       (GTK_RADIO_BUTTON (tmp))));
 
 	source->priv->library_location_entry = glade_xml_get_widget (xml, "library_location_entry");
 	source->priv->watch_library_check = glade_xml_get_widget (xml, "watch_library_check");
@@ -712,7 +714,6 @@
 			  G_CALLBACK (rb_library_source_location_button_clicked_cb),
 			  asource);
 
-	rb_glade_boldify_label (xml, "browser_views_label");
 	rb_glade_boldify_label (xml, "library_location_label");
 	g_object_unref (G_OBJECT (xml));
 	
@@ -765,10 +766,6 @@
 
 	rb_debug ("syncing pref dialog state");
 
-	list = g_slist_nth (source->priv->browser_views_group,
-			    eel_gconf_get_integer (CONF_UI_BROWSER_VIEWS));
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (list->data), TRUE);
-
 	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
 
 	/* don't trigger the change notification */
@@ -841,20 +838,6 @@
 }
 
 
-void
-rb_library_source_browser_views_activated_cb (GtkWidget *widget,
-					    RBLibrarySource *source)
-{
-	int index;
-
-	if (source->priv->loading_prefs == TRUE)
-		return;
-
-	index = g_slist_index (source->priv->browser_views_group, widget);
-
-	eel_gconf_set_integer (CONF_UI_BROWSER_VIEWS, index);
-}
-
 static void
 impl_delete (RBSource *asource)
 {
@@ -1021,36 +1004,39 @@
 impl_receive_drag (RBSource *asource, GtkSelectionData *data)
 {
 	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
-	GList *list, *uri_list, *i;
+	GList *list, *i;
+	GList *entries = NULL;
 
 	rb_debug ("parsing uri list");
-	list = gnome_vfs_uri_list_parse ((char *) data->data);
-
-	if (list == NULL)
-		return FALSE;
+	list = rb_uri_list_parse ((const char *) data->data);
 
-	uri_list = NULL;
+	for (i = list; i != NULL; i = g_list_next (i)) {
+		if (i->data != NULL) {
+			char *uri = i->data;
+			RhythmDBEntry *entry;
 
-	for (i = list; i != NULL; i = g_list_next (i))
-		uri_list = g_list_prepend (uri_list, gnome_vfs_uri_to_string ((const GnomeVFSURI *) i->data, 0));
+			entry = rhythmdb_entry_lookup_by_location (source->priv->db, uri);
 
-	gnome_vfs_uri_list_free (list);
-
-	if (uri_list == NULL)
-		return FALSE;
-	
-	rb_debug ("adding uris");
-
-	for (i = uri_list; i != NULL; i = i->next) {
-		char *uri = i->data;
+			if (entry == NULL) {
+				/* add to the library */
+				rhythmdb_add_uri (source->priv->db, uri);
+			} else {
+				/* add to list of entries to copy */
+				entries = g_list_prepend (entries, entry);
+			}
 
-		if (uri != NULL)
-			rhythmdb_add_uri (source->priv->db, uri);
+			g_free (uri);
+		}
+	}
 
-		g_free (uri);
+	if (entries) {
+		entries = g_list_reverse (entries);
+		if (rb_source_can_paste (asource))
+			rb_source_paste (asource, entries);
+		g_list_free (entries);
 	}
 
-	g_list_free (uri_list);
+	g_list_free (list);
 	return TRUE;
 }
 
@@ -1178,3 +1164,321 @@
 	rb_library_source_do_query (source);
 	rb_source_notify_filter_changed (RB_SOURCE (source));
 }
+
+
+/**
+ * Perform magic on a path to make it safe.
+ *
+ * This will always replace '/' with ' ', and optionally make the file name
+ * shell-friendly. This involves removing [?*\ ] and replacing with '_'.  Also
+ * any leading periods are removed so that the files don't end up being hidden.
+ *
+ * This function doesn't change the input, and returns an allocated 
+ * string.
+ */
+static char*
+sanitize_path (const char* str)
+{
+  gchar *res = NULL;
+  gchar *s;
+
+  /* Skip leading periods, otherwise files disappear... */
+  while (*str == '.')
+    str++;
+  
+  s = g_strdup(str);
+  /* Replace path seperators with a hyphen */
+  g_strdelimit (s, "/", '-');
+  if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS)) {
+    /* Replace separators with a hyphen */
+    g_strdelimit (s, "\\:|", '-');
+    /* Replace all other weird characters to whitespace */
+    g_strdelimit (s, "*?&!\'\"$()`>{}", ' ');
+    /* Replace all whitespace with underscores */
+    /* TODO: I'd like this to compress whitespace aswell */
+    g_strdelimit (s, "\t ", '_');
+  }
+  res = g_filename_from_utf8(s, -1, NULL, NULL, NULL);
+  g_free(s);
+  return res ? res : g_strdup(str);
+}
+
+
+/**
+ * Parse a filename pattern and replace markers with values from a TrackDetails
+ * structure.
+ *
+ * Valid markers so far are:
+ * %at -- album title
+ * %aa -- album artist
+ * %aA -- album artist (lowercase)
+ * %as -- album artist sortname
+ * %aS -- album artist sortname (lowercase)
+ * %tn -- track number (i.e 8)
+ * %tN -- track number, zero padded (i.e 08)
+ * %tt -- track title
+ * %ta -- track artist
+ * %tA -- track artist (lowercase)
+ * %ts -- track artist sortname
+ * %tS -- track artist sortname (lowercase)
+ */
+static char*
+filepath_parse_pattern (const char* pattern, RhythmDBEntry *entry)
+{
+  /* p is the pattern iterator, i is a general purpose iterator */
+  const char *p, *i;
+  /* string is the output string, s is the iterator, t is a general string */
+  char *string, *s, *temp;
+  s = string = g_new0 (char, 256); /* TODO: bad hardcoding */
+
+  p = pattern;
+  while (*p) {
+    /* If not a % marker, copy and continue */
+    if (*p != '%') {
+      *s++ = *p++;
+      /* Explicit increment as we continue past the increment */
+      continue;
+    }
+    /* Is a % marker, go to next and see what to do */
+    switch (*++p) {
+    case '%':
+      /*
+       * Literal %
+       */
+      *s++ = '%';
+      break;
+    case 'a':
+      /*
+       * Album tag
+       */
+      switch (*++p) {
+      case 't':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'T':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_FOLDED));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'a':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'A':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_FOLDED));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      /*case 's':
+        i = temp = sanitize_path (track->album->artist_sortname);
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'S':
+	tmp = g_utf8_strdown (track->album->artist_sortname, -1);
+	i = temp = sanitize_path (tmp);
+	g_free(tmp);
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;*/
+      default:
+        *s++ = '%'; *s++ = 'a'; *s++ = *p;
+      }
+      break;
+    case 't':
+      /*
+       * Track tag
+       */
+      switch (*++p) {
+      case 't':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'T':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE_FOLDED));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'a':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'A':
+        i = temp = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST_FOLDED));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      /*case 's':
+        i = temp = sanitize_path (track->artist_sortname);
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'S':
+	tmp = g_utf8_strdown (track->artist_sortname, -1);
+        i = temp = sanitize_path (tmp);
+	g_free(tmp);
+	while (*i) *s++ = *i++;
+	g_free (temp);
+	break;*/
+      case 'n':
+        /* Track number */
+        i = temp = g_strdup_printf ("%u", (guint)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      case 'N':
+        /* Track number, zero-padded */
+        i = temp = g_strdup_printf ("%02u", (guint)rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER));
+        while (*i) *s++ = *i++;
+        g_free (temp);
+        break;
+      default:
+        *s++ = '%'; *s++ = 't'; *s++ = *p;
+      }
+      break;
+    default:
+      *s++ = '%';
+      *s++ = *p;
+    }
+    ++p;
+  }
+  *s = '\0';
+  return string;
+}
+
+/**
+ * Build the absolute filename for the specified track.
+ *
+ * The base path is the extern variable 'base_path', the format to use
+ * is the extern variable 'file_pattern'. Free the result when you
+ * have finished with it.
+ *
+ * Stolen from Sound-Juicer
+ */
+static char*
+build_filename (RBLibrarySource *source, RhythmDBEntry *entry)
+{
+	GnomeVFSURI *uri, *new;
+	char *realfile, *realpath, *filename, *string;
+	char *extension;
+	GSList *list;
+
+
+	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	if (list == NULL) {
+		/* emit warning */
+		return NULL;
+	}
+
+	uri = gnome_vfs_uri_new ((const char *)list->data);
+	g_slist_free (list);
+
+	realpath = filepath_parse_pattern (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH), entry);
+	new = gnome_vfs_uri_append_path (uri, realpath);
+	gnome_vfs_uri_unref (uri); uri = new;
+	g_free (realpath);
+
+	if (g_str_has_prefix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE), "audio/x-raw")) {
+		GMAudioProfile *profile;
+		profile = gm_audio_profile_lookup (eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT));
+		extension = g_strdup (gm_audio_profile_get_extension (profile));
+	} else {
+		char *tmp;
+		
+		/* use the old extension. strip anything after a '?' for http/daap/etc */
+		extension = g_strdup (g_utf8_strrchr (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), -1, '.') + 1);
+
+		tmp = g_utf8_strchr (extension, -1, '?');
+		if (tmp)
+			*tmp = '\0';
+	}
+	
+	realfile = filepath_parse_pattern (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME), entry);
+	if (extension) {
+		filename = g_strdup_printf ("%s.%s", realfile, extension);
+		g_free (realfile);
+	} else {
+		filename = realfile;
+	}
+
+	new = gnome_vfs_uri_append_file_name (uri, filename);
+	gnome_vfs_uri_unref (uri); uri = new;
+	g_free (extension);
+	g_free (filename);
+
+	string = gnome_vfs_uri_to_string (uri, 0);
+	gnome_vfs_uri_unref (uri);
+	return string;
+}
+
+static gboolean
+impl_can_paste (RBSource *asource)
+{
+	GSList *list;
+
+	list = eel_gconf_get_string_list (CONF_LIBRARY_LOCATION);
+	if (list == NULL)
+		return FALSE;
+
+	g_slist_free (list);
+	return TRUE;
+}
+
+static void
+error_cb (RBEncoder *encoder, RBLibrarySource *source, GError *error)
+{
+	rb_error_dialog (NULL, _("Error transferring track"), "%s", error->message);
+	g_object_unref (G_OBJECT (encoder));
+}
+
+static void
+completed_cb (RBEncoder *encoder, RBLibrarySource *source)
+{
+	char *dest;
+
+	dest = g_object_get_data (G_OBJECT (encoder), "rb-destination");
+	
+	rhythmdb_add_uri (source->priv->db, dest);
+	g_free (dest);
+
+	g_object_unref (G_OBJECT (encoder));
+}
+
+static void
+impl_paste (RBSource *asource, GList *entries)
+{
+	RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
+	GList *l;
+
+
+	
+	for (l = entries; l != NULL; l = g_list_next (l)) {
+		RhythmDBEntry *entry = (RhythmDBEntry *)l->data;
+		RBEncoder *encoder;
+		char *dest;
+
+		dest = build_filename (source, entry);
+		if (dest == NULL) {
+			rb_debug ("could not create destination path for entry");
+			continue;
+		}
+
+		encoder = rb_encoder_new ();
+
+		g_signal_connect_object (G_OBJECT (encoder),
+					 "error", G_CALLBACK (error_cb),
+					 asource, 0);
+		g_signal_connect_object (G_OBJECT (encoder),
+					 "completed", G_CALLBACK (completed_cb),
+					 asource, 0);
+
+		g_object_set_data (G_OBJECT (encoder), "rb-destination", dest);
+		rb_encoder_encode (encoder, entry, dest, NULL);
+	}
+}
+
diff -u -u -r1.10 rb-removable-media-source.c
--- sources/rb-removable-media-source.c	9 Feb 2006 21:24:51 -0000	1.10
+++ sources/rb-removable-media-source.c	26 Feb 2006 13:35:50 -0000
@@ -87,7 +87,8 @@
 
 	source_class->impl_delete_thyself = impl_delete_thyself;
 	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
-	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
 	source_class->impl_receive_drag = NULL;
 	source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
diff -u -u -r1.59 rb-source.c
--- sources/rb-source.c	22 Feb 2006 13:06:02 -0000	1.59
+++ sources/rb-source.c	26 Feb 2006 13:35:50 -0000
@@ -126,6 +126,7 @@
 	klass->impl_can_rename = default_can_rename;
 	klass->impl_can_search = default_can_search;
 	klass->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	klass->impl_can_paste = (RBSourceFeatureFunc) rb_false_function;
 	klass->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
 	klass->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
 	klass->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_false_function;
@@ -575,6 +576,14 @@
 	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
 
 	return klass->impl_can_cut (source);
+}
+
+gboolean
+rb_source_can_paste (RBSource *source)
+{
+	RBSourceClass *klass = RB_SOURCE_GET_CLASS (source);
+
+	return klass->impl_can_paste (source);
 }
 
 gboolean
diff -u -u -r1.38 rb-source.h
--- sources/rb-source.h	22 Feb 2006 13:06:02 -0000	1.38
+++ sources/rb-source.h	26 Feb 2006 13:35:50 -0000
@@ -90,6 +90,7 @@
 	gboolean	(*impl_can_delete)	(RBSource *source);
 	gboolean	(*impl_can_move_to_trash) (RBSource *source);
 	gboolean	(*impl_can_copy)	(RBSource *source);
+	gboolean	(*impl_can_paste)	(RBSource *source);
 	gboolean	(*impl_can_add_to_queue)(RBSource *source);
 	
 	GList *		(*impl_cut)		(RBSource *source);
@@ -152,6 +153,7 @@
 gboolean	rb_source_can_delete		(RBSource *source);
 gboolean	rb_source_can_move_to_trash	(RBSource *source);
 gboolean	rb_source_can_copy		(RBSource *source);
+gboolean	rb_source_can_paste		(RBSource *source);
 gboolean	rb_source_can_add_to_queue	(RBSource *source);
 
 GList *		rb_source_cut			(RBSource *source);
diff -u -u -r1.19 rb-sourcelist-model.c
--- sources/rb-sourcelist-model.c	22 Feb 2006 13:06:02 -0000	1.19
+++ sources/rb-sourcelist-model.c	26 Feb 2006 13:35:50 -0000
@@ -345,7 +345,7 @@
 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
 				    RB_SOURCELIST_MODEL_COLUMN_SOURCE, &source, -1);
 		
-		return rb_source_can_cut (source);
+		return rb_source_can_paste (source);
 
 	}
 	return FALSE;
diff -u -u -r1.11 rb-static-playlist-source.c
--- sources/rb-static-playlist-source.c	17 Feb 2006 15:05:48 -0000	1.11
+++ sources/rb-static-playlist-source.c	26 Feb 2006 13:35:50 -0000
@@ -92,7 +92,7 @@
 	object_class->finalize = rb_static_playlist_source_finalize;
 
 	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_true_function;
-	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
 	source_class->impl_cut = impl_cut;
 	source_class->impl_paste = impl_paste;
--- /dev/null	2006-02-26 17:05:38.000740840 +1100
+++ backends/Makefile.am	2006-02-24 18:52:33.000000000 +1100
@@ -0,0 +1,29 @@
+## arch-tag: Automake rules for backends library
+
+noinst_LTLIBRARIES = librbbackends.la
+
+SUBDIRS = gstreamer
+
+librbbackends_la_SOURCES =	\
+	rb-encoder.h 		\
+	rb-encoder.c
+
+librbbackends_la_LIBADD =				\
+	$(RHYTHMBOX_LIBS)				\
+	$(top_builddir)/backends/gstreamer/librbbackendsgstreamer.la
+
+librbbackends_la_LDFLAGS = -export-dynamic
+
+INCLUDES = 						\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"			\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib 				\
+	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/shell				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/widgets				\
+	-I$(top_srcdir)/backends/gstreamer		\
+	$(LIBNAUTILUS_BURN_CFLAGS)			\
+	$(RHYTHMBOX_CFLAGS)
+
--- /dev/null	2006-02-26 17:05:38.000740840 +1100
+++ backends/rb-encoder.h	2006-02-27 00:33:41.000000000 +1100
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * arch-tag: Interface to audio encoder backend  
+ *
+ * Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ * 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_ENCODER_H__
+#define __RB_ENCODER_H__
+
+#include <glib-object.h>
+
+#include "rhythmdb.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_ENCODER            (rb_encoder_get_type ())
+#define RB_ENCODER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_ENCODER, RBEncoder))
+#define RB_IS_ENCODER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_ENCODER))
+#define RB_ENCODER_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), RB_TYPE_ENCODER, RBEncoderIface))
+
+
+enum
+{
+	RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
+	RB_ENCODER_ERROR_INTERNAL,
+};
+
+#define RB_ENCODER_ERROR rb_encoder_error_quark ()
+
+GQuark rb_encoder_error_quark (void);
+
+typedef struct _RBEncoder RBEncoder;
+
+typedef struct
+{
+	GTypeInterface g_iface;
+
+	/* vtable */
+	void	(*encode)		(RBEncoder *encoder,
+					 RhythmDBEntry *entry,
+					 const char *dest,
+					 const char *mime_type);
+
+	/* signals */
+	void (*progress) (RBEncoder *encoder,  guint done);
+	void (*completed) (RBEncoder *encoder);
+	void (*error) (RBEncoder *encoder, GError *error);
+} RBEncoderIface;
+
+RBEncoder*	rb_encoder_new		(void);
+GType rb_encoder_get_type (void);
+
+void		rb_encoder_encode	(RBEncoder *encoder,
+					 RhythmDBEntry *entry,
+					 const char *dest,
+					 const char *mime_type);
+
+/* obly to be used by subclasses */
+void	_rb_encoder_emit_progress (RBEncoder *encoder, guint done);
+void	_rb_encoder_emit_completed (RBEncoder *encoder);
+void	_rb_encoder_emit_error (RBEncoder *encoder, GError *error);
+
+G_END_DECLS
+
+#endif /* __RB_ENCODER_H__ */
--- /dev/null	2006-02-26 17:05:38.000740840 +1100
+++ backends/rb-encoder.c	2006-02-27 00:25:20.000000000 +1100
@@ -0,0 +1,137 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2006  James Livingston  <jrl@ids.org.au>
+ *
+ *  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 <config.h>
+
+#include "rb-encoder.h"
+#include "rb-encoder-gst.h"
+
+/* Signals */
+enum {
+	PROGRESS,
+	COMPLETED,
+	ERROR,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+rb_encoder_interface_init (RBEncoderIface *iface)
+{
+	signals[PROGRESS] =
+		g_signal_new ("progress",
+			      G_TYPE_FROM_INTERFACE (iface),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBEncoderIface, progress),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__INT,
+			      G_TYPE_NONE,
+			      1, G_TYPE_UINT);
+	signals[COMPLETED] =
+		g_signal_new ("completed",
+			      G_TYPE_FROM_INTERFACE (iface),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBEncoderIface, completed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	signals[ERROR] =
+		g_signal_new ("error",
+			      G_TYPE_FROM_INTERFACE (iface),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (RBEncoderIface, error),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__POINTER,
+			      G_TYPE_NONE,
+			      1, G_TYPE_POINTER);
+
+}
+
+GType
+rb_encoder_get_type (void)
+{
+	static GType our_type = 0;
+
+	if (!our_type) {
+		static const GTypeInfo our_info = {
+			sizeof (RBEncoderIface),
+			NULL,	/* base_init */
+			NULL,	/* base_finalize */
+			(GClassInitFunc)rb_encoder_interface_init,
+			NULL,	/* class_finalize */
+			NULL,	/* class_data */
+			0,
+			0,
+			NULL
+		};
+
+		our_type = g_type_register_static (G_TYPE_INTERFACE, "RBEncoder", &our_info, 0);
+	}
+
+	return our_type;
+}
+
+
+void
+rb_encoder_encode (RBEncoder *encoder,
+		   RhythmDBEntry *entry,
+		   const char *dest,
+		   const char *mime_type)
+{
+	RBEncoderIface *iface = RB_ENCODER_GET_IFACE (encoder);
+
+	iface->encode (encoder, entry, dest, mime_type);	  	 
+}
+
+RBEncoder*
+rb_encoder_new (void)
+{
+	return rb_encoder_gst_new ();
+}
+
+void
+_rb_encoder_emit_progress (RBEncoder *encoder, guint done)
+{
+	g_signal_emit (encoder, signals[PROGRESS], 0, done);
+}
+
+void
+_rb_encoder_emit_completed (RBEncoder *encoder)
+{
+	g_signal_emit (encoder, signals[COMPLETED], 0);
+}
+
+void
+_rb_encoder_emit_error (RBEncoder *encoder, GError *error)
+{
+	g_signal_emit (encoder, signals[ERROR], 0, error);
+}
+
+GQuark
+rb_encoder_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("rb_encoder_error");
+
+	return quark;
+}
--- /dev/null	2006-02-26 17:05:38.000740840 +1100
+++ backends/gstreamer/Makefile.am	2006-02-24 18:41:13.000000000 +1100
@@ -0,0 +1,27 @@
+## arch-tag: Automake rules for gstreamer backend library
+
+noinst_LTLIBRARIES = librbbackendsgstreamer.la
+
+librbbackendsgstreamer_la_SOURCES =	\
+	rb-encoder-gst.h		\
+	rb-encoder-gst.c
+
+librbbackendsgstreamer_la_LIBADD =				\
+	$(RHYTHMBOX_LIBS)
+
+librbbackendsgstreamer_la_LDFLAGS = -export-dynamic
+
+INCLUDES = 						\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"			\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib 				\
+	-I$(top_srcdir)/metadata			\
+	-I$(top_srcdir)/sources				\
+	-I$(top_srcdir)/rhythmdb			\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/widgets				\
+	$(LIBJUICER_CFLAGS)				\
+	$(RHYTHMBOX_CFLAGS)
+
+
--- /dev/null	2006-02-26 17:05:38.000740840 +1100
+++ backends/gstreamer/rb-encoder-gst.h	2006-02-24 19:01:04.000000000 +1100
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * arch-tag: Interface to audio encoder backend  
+ *
+ * Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ * 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_ENCODER_GST_H__
+#define __RB_ENCODER_GST_H__
+
+#include <glib-object.h>
+
+#include "rb-encoder.h"
+
+G_BEGIN_DECLS
+
+
+#define RB_TYPE_ENCODER_GST            (rb_encoder_gst_get_type ())
+#define RB_ENCODER_GST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_ENCODER, RBEncoderGst))
+#define RB_IS_ENCODER_GST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_ENCODER))
+
+typedef struct _RBEncoderGstPrivate RBEncoderGstPrivate;
+
+typedef struct
+{
+	GObjectClass obj_class;
+} RBEncoderGstClass;
+
+typedef struct
+{
+	GObject obj;
+	RBEncoderGstPrivate *priv;
+} RBEncoderGst;
+
+RBEncoder*	rb_encoder_gst_new		(void);
+GType rb_encoder_gst_get_type (void);
+
+
+G_END_DECLS
+
+#endif /* __RB_ENCODER_GST_H__ */
--- /dev/null	2006-02-26 17:05:38.000740840 +1100
+++ backends/gstreamer/rb-encoder-gst.c	2006-02-27 00:47:28.000000000 +1100
@@ -0,0 +1,504 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * arch-tag: Implementation of GStreamer encoding backend
+ * 
+ * Based on Sound-Juicer's ripping code
+ *
+ * Copyright (C) 2003 Ross Burton <ross@burtonini.com>
+ * Copyright (C) 2006 James Livingston <jrl@ids.org.au>
+ *
+ * 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 <config.h>
+
+#include <glib/gi18n.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <libgnomevfs/gnome-vfs-uri.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <gst/gst.h>
+#include <string.h>
+#include <profiles/gnome-media-profiles.h>
+
+#include "rhythmdb.h"
+#include "eel-gconf-extensions.h"
+#include "rb-preferences.h"
+#include "rb-encoder.h"
+#include "rb-encoder-gst.h"
+#include "rb-debug.h"
+
+
+static void rb_encoder_gst_class_init (RBEncoderGstClass *klass);
+static void rb_encoder_gst_init       (RBEncoderGst *encoder);
+static void rb_encoder_gst_finalize   (GObject *object);
+static void rb_encoder_init (RBEncoderIface *iface);
+
+
+struct _RBEncoderGstPrivate {
+	GstElement *pipeline;
+};
+
+G_DEFINE_TYPE_WITH_CODE(RBEncoderGst, rb_encoder_gst, G_TYPE_OBJECT,
+			G_IMPLEMENT_INTERFACE(RB_TYPE_ENCODER,
+					      rb_encoder_init))
+#define RB_ENCODER_GST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_ENCODER_GST, RBEncoderGstPrivate))
+
+
+static void rb_encoder_gst_encode (RBEncoder *encoder,
+				   RhythmDBEntry *entry,
+				   const char *dest,
+				   const char *mime_type);
+	
+static void
+rb_encoder_gst_class_init (RBEncoderGstClass *klass)
+{
+        GObjectClass *object_class = (GObjectClass *) klass;
+
+        object_class->finalize = rb_encoder_gst_finalize;
+
+        g_type_class_add_private (klass, sizeof (RBEncoderGstPrivate));
+}
+
+static void
+rb_encoder_gst_init (RBEncoderGst *encoder)
+{
+        encoder->priv = RB_ENCODER_GST_GET_PRIVATE (encoder);
+}
+
+static void
+rb_encoder_init (RBEncoderIface *iface)
+{
+	iface->encode = rb_encoder_gst_encode;
+}
+
+static void
+rb_encoder_gst_finalize (GObject *object)
+{
+	RBEncoderGst *encoder = RB_ENCODER_GST (object);
+	
+	if (encoder->priv->pipeline) {
+		gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
+		g_object_unref (encoder->priv->pipeline);
+		encoder->priv->pipeline = NULL;
+	}	
+
+        G_OBJECT_CLASS (rb_encoder_gst_parent_class)->finalize (object);
+}
+
+RBEncoder*
+rb_encoder_gst_new (void)
+{
+	return RB_ENCODER (g_object_new (RB_TYPE_ENCODER_GST, NULL));
+}
+
+
+static gboolean
+bus_watch_cb (GstBus *bus, GstMessage *message, gpointer data)
+{
+	RBEncoderGst *encoder = RB_ENCODER_GST (data);
+	char *string;
+	GError *error;
+
+	/* ref ourselves, in case one of the signal handler unrefs us */
+	g_object_ref (G_OBJECT (encoder));
+
+	switch (GST_MESSAGE_TYPE (message)) {
+	case GST_MESSAGE_ERROR:
+		gst_message_parse_error (message, &error, &string);
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		rb_debug ("received error %s", string);
+		g_error_free (error);
+		g_free (string);
+		
+		/* Make sure the pipeline is not running any more */
+		gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
+		g_object_unref (encoder->priv->pipeline);
+		encoder->priv->pipeline = NULL;
+
+		/* should be emit completed here? */
+		break;
+
+	case GST_MESSAGE_WARNING:
+		gst_message_parse_warning (message, &error, &string);
+		rb_debug ("received warning %s", string);
+		g_error_free (error);
+		g_free (string);
+		break;
+	
+	case GST_MESSAGE_EOS:
+		rb_debug ("received EOS");
+
+		gst_element_set_state (encoder->priv->pipeline, GST_STATE_NULL);
+
+		_rb_encoder_emit_completed (RB_ENCODER (encoder));
+
+		g_object_unref (encoder->priv->pipeline);
+		encoder->priv->pipeline = NULL;
+		break;
+	
+	default:
+		rb_debug ("message of type %s", gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
+		break;
+	}
+
+	g_object_unref (G_OBJECT (encoder));
+	return FALSE;
+}
+
+static void
+start_pipeline (RBEncoderGst *encoder)
+{
+	GstBus *bus;
+
+	g_assert (encoder->priv->pipeline);
+	
+	bus = gst_pipeline_get_bus (GST_PIPELINE (encoder->priv->pipeline));
+	gst_bus_add_watch (bus, bus_watch_cb, encoder);
+
+	gst_element_set_state (encoder->priv->pipeline, GST_STATE_PLAYING);
+}
+
+static GstElement*
+add_encoding_pipeline (RBEncoderGst *encoder,
+		       GstElement *start,
+		       GMAudioProfile *profile)
+{
+	GstElement *queue, *encoding_bin;
+	GstPad *pad;
+	char *tmp;
+	GError *error = NULL;
+
+	/* Nice big buffers... */
+	queue = gst_element_factory_make ("queue", "queue");
+	if (queue == NULL)
+		return NULL;
+	g_object_set (queue, "max-size-time", 120 * GST_SECOND, NULL);
+	gst_bin_add (GST_BIN (encoder->priv->pipeline), queue);
+	gst_element_link (start, queue);
+
+	tmp = g_strdup_printf ("audioconvert ! %s", gm_audio_profile_get_pipeline (profile));
+	encoding_bin = GST_ELEMENT (gst_parse_launch (tmp, &error));
+	g_free (tmp);
+
+	if (encoding_bin == NULL)
+		return NULL;
+
+	/* find pads and ghost them if necessary */
+	if ((pad = gst_bin_find_unconnected_pad (GST_BIN (encoding_bin), GST_PAD_SRC)))
+		gst_element_add_pad (encoding_bin, gst_ghost_pad_new ("src", pad));
+	if ((pad = gst_bin_find_unconnected_pad (GST_BIN (encoding_bin), GST_PAD_SINK)))
+		gst_element_add_pad (encoding_bin, gst_ghost_pad_new ("sink", pad));
+
+	gst_bin_add (GST_BIN (encoder->priv->pipeline), encoding_bin);
+	gst_element_link (queue, encoding_bin);
+
+	return encoding_bin;
+}
+
+static void
+add_tags_from_entry (RBEncoderGst *encoder,
+		     RhythmDBEntry *entry)
+{
+	GstTagList *tags;
+	GstIterator *iter;
+	gboolean done;
+
+	tags = gst_tag_list_new ();
+	gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE_ALL,
+			  /* FIXME: add the rest of them */
+			  GST_TAG_TITLE, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE),
+			  GST_TAG_ARTIST, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
+			  GST_TAG_TRACK_NUMBER, rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
+			  GST_TAG_ALBUM, rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM),
+			  GST_TAG_ENCODER, "Rhythmbox",
+			  GST_TAG_ENCODER_VERSION, VERSION,
+			  NULL);
+
+	iter = gst_bin_iterate_all_by_interface (GST_BIN (encoder->priv->pipeline), GST_TYPE_TAG_SETTER);
+	done = FALSE;
+	while (!done) {
+		GstTagSetter *tagger = NULL;
+
+		switch (gst_iterator_next (iter, (gpointer*)&tagger)) {
+		case GST_ITERATOR_OK:
+			gst_tag_setter_merge_tags (tagger, tags, GST_TAG_MERGE_REPLACE_ALL);
+			break;
+		case GST_ITERATOR_RESYNC:
+			gst_iterator_resync (iter);
+			break;
+		case GST_ITERATOR_ERROR:
+		{
+			GError *error;
+		
+			error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
+					     "Could not add tags to tag-setter");
+			_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+			g_error_free (error);
+			done = TRUE;
+			break;
+		}
+		case GST_ITERATOR_DONE:
+			done = TRUE;
+			break;
+		}
+		
+		if (tagger)
+			gst_object_unref (tagger);
+	}
+	gst_iterator_free (iter);
+	gst_tag_list_free (tags);
+}
+
+static void
+attach_output_pipeline (RBEncoderGst *encoder,
+			GstElement *end,
+			const char *dest)
+{
+	GstElement *sink;
+
+	sink = gst_element_make_from_uri (GST_URI_SINK, dest, "sink");
+	gst_bin_add (GST_BIN (encoder->priv->pipeline), sink);
+	gst_element_link (end, sink);
+}
+
+static GMAudioProfile*
+get_profile_from_mime_type (const char *mime_type)
+{
+	/* TODO */
+	return NULL;
+}
+
+static GstElement *
+create_pipeline_and_source (RBEncoderGst *encoder,
+			    RhythmDBEntry *entry)
+{
+	const char *uri;
+	GstElement *src;
+
+	uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
+	src = gst_element_make_from_uri (GST_URI_SRC, uri, "source");
+	if (src == NULL) {
+		GError *error;
+		
+		error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
+				     "could not create source element for '%s'", uri);
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		g_error_free (error);
+		return NULL;
+	}
+
+	encoder->priv->pipeline = gst_pipeline_new ("pipeline");
+	gst_bin_add (GST_BIN (encoder->priv->pipeline), src);
+
+	return src;
+}
+
+static void
+copy_track (RBEncoderGst *encoder,
+	    RhythmDBEntry *entry,
+	    const char *dest)
+{
+	/* source ! sink */
+	GstElement *src;
+
+	g_assert (encoder->priv->pipeline == NULL);
+
+	src = create_pipeline_and_source (encoder, entry);
+	if (src == NULL)
+		return;
+
+	attach_output_pipeline (encoder, src, dest);
+	start_pipeline (encoder);
+}
+
+static void
+extract_track (RBEncoderGst *encoder,
+	       RhythmDBEntry *entry,
+	       const char *dest)
+{
+	/* cdsrc ! encoder ! sink */
+	const char *uri;
+	const char *device;
+	const char *profile_name;
+	GMAudioProfile *profile;
+	GstElement *src, *end;
+
+	g_assert (encoder->priv->pipeline == NULL);
+
+	profile_name = eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT);
+	profile = gm_audio_profile_lookup (profile_name);
+	if (profile == NULL) {
+		GError *error;
+		
+		error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
+				     "Could not find encoding profile '%s'", profile_name);
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		g_error_free (error);
+		return;					
+	}
+
+	src = create_pipeline_and_source (encoder, entry);
+	if (src == NULL)
+		return;
+
+	/* setup cd extraction properties */
+	device = g_utf8_strrchr (uri, -1, '#');
+	g_object_set (G_OBJECT (src),
+		      "device", device + 1, /* skip the '#' */
+		      "track", rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER),
+		      NULL);
+	/*if (g_object_class_find_property (G_OBJECT_GET_CLASS (src), "paranoia-mode")) {
+		int paranoia_mode;
+
+		paranoia_mode = FOO;
+		g_object_set (G_OBJECT (src), "paranoia-mode", paranoia_mode, NULL);
+	}*/
+
+	end = add_encoding_pipeline (encoder, src, profile);
+	if (end == NULL) {
+		GError *error;
+		
+		error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
+				     "Could not create encoding profile '%s'", profile_name);
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		g_error_free (error);
+		
+		g_object_unref (encoder->priv->pipeline);
+		encoder->priv->pipeline = NULL;
+		return;
+	}
+	attach_output_pipeline (encoder, end, dest);
+
+	add_tags_from_entry (encoder, entry);
+	start_pipeline (encoder);
+}
+
+static void
+transcode_track (RBEncoderGst *encoder,
+	 	 RhythmDBEntry *entry,
+		 const char *dest,
+		 const char *mime_type)
+{
+	/* cdsrc ! encoder ! sink */
+	GMAudioProfile *profile;
+	GstElement *src, *spider, *end;
+
+	g_assert (encoder->priv->pipeline == NULL);
+
+	profile = get_profile_from_mime_type (mime_type);
+	if (profile == NULL) {
+		GError *error;
+		
+		error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_FORMAT_UNSUPPORTED,
+				     "Unable to locate encoding profile for mime-type '%s'", mime_type);
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		g_error_free (error);
+		return;
+	}
+
+	src = create_pipeline_and_source (encoder, entry);
+	if (src == NULL)
+		return;
+
+	spider = gst_element_factory_make ("spider", "spider");
+	if (spider == NULL) {
+		GError *error;
+		
+		error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
+				     "Could not create encoding profile '%s'", gm_audio_profile_get_id (profile));
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		g_error_free (error);
+		
+		g_object_unref (encoder->priv->pipeline);
+		encoder->priv->pipeline = NULL;
+		return;
+	}
+	gst_bin_add (GST_BIN (encoder->priv->pipeline), spider);
+	gst_element_link (src, spider);
+
+	end = add_encoding_pipeline (encoder, spider, profile);
+	if (end == NULL) {
+		GError *error;
+		
+		error = g_error_new (RB_ENCODER_ERROR, RB_ENCODER_ERROR_INTERNAL,
+				     "Could not create encoding profile '%s'", gm_audio_profile_get_id (profile));
+		_rb_encoder_emit_error (RB_ENCODER (encoder), error);
+		g_error_free (error);
+		
+		g_object_unref (encoder->priv->pipeline);
+		encoder->priv->pipeline = NULL;
+		return;
+	}
+	attach_output_pipeline (encoder, end, dest);
+
+	add_tags_from_entry (encoder, entry);
+	start_pipeline (encoder);
+}
+
+static void
+create_parent_dirs_uri (GnomeVFSURI *uri)
+{
+	GnomeVFSURI *parent_uri;
+
+	if (gnome_vfs_uri_exists (uri))
+		return;
+
+	parent_uri = gnome_vfs_uri_get_parent (uri);
+	create_parent_dirs_uri (parent_uri);
+	gnome_vfs_uri_unref (parent_uri);
+
+	gnome_vfs_make_directory_for_uri (uri, 0750);
+}
+
+static void
+create_parent_dirs (const char *uri)
+{
+	GnomeVFSURI *vfs_uri;
+	GnomeVFSURI *parent_uri;
+
+	vfs_uri = gnome_vfs_uri_new (uri);
+	parent_uri = gnome_vfs_uri_get_parent (vfs_uri);
+
+	create_parent_dirs_uri (parent_uri);
+
+	gnome_vfs_uri_unref (parent_uri);
+	gnome_vfs_uri_unref (vfs_uri);
+}
+
+static void
+rb_encoder_gst_encode (RBEncoder *encoder,
+		       RhythmDBEntry *entry,
+		       const char *dest,
+		       const char *mime_type)
+{
+	const char *entry_mime_type;
+	gboolean was_raw;
+	
+	entry_mime_type = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_MIMETYPE);
+	was_raw = g_str_has_prefix (entry_mime_type, "audio/x-raw");
+
+	create_parent_dirs (dest);
+
+	if ((mime_type == NULL && !was_raw) || (mime_type && (strcmp (mime_type, entry_mime_type) == 0))) {
+		copy_track (RB_ENCODER_GST (encoder), entry, dest);
+	} else {
+		if (mime_type == NULL) {
+			extract_track (RB_ENCODER_GST (encoder), entry, dest);
+		} else {
+			transcode_track (RB_ENCODER_GST (encoder), entry, dest, mime_type);
+		}
+	}
+}
