banshee r3580 - in trunk/banshee: . build build/pkg-config src/Core/Banshee.Core/Resources src/Extensions/Banshee.AudioCd src/Extensions/Banshee.AudioCd/Banshee.AudioCd src/Libraries src/Libraries/MusicBrainz src/Libraries/MusicBrainz/MusicBrainz



Author: abock
Date: Sat Mar 29 00:05:08 2008
New Revision: 3580
URL: http://svn.gnome.org/viewvc/banshee?rev=3580&view=rev

Log:
2008-03-28  Aaron Bockover  <abock gnome org>

    * build/build.environment.mk:
    * build/pkg-config/banshee-1-core.pc.in:
    * build/pkg-config/banshee-1-musicbrainz.pc.in:
    * configure.ac:
    * src/Libraries/Libraries.mds:
    * src/Libraries/Makefile.am:
    * src/Libraries/MusicBrainz/: Added Scott's musicbrainz-sharp code locally
    to the project for now; in the future we will just depend on the library
    but for now we'll incubate it

    * src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs:
    Renamed from AudioCdDisc, inhereit MemoryTrackListModel

    * src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs:
    * src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs:
    Changes to reflect the model stuff



Added:
   trunk/banshee/build/pkg-config/banshee-1-musicbrainz.pc.in
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs
      - copied, changed from r3578, /trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDisc.cs
   trunk/banshee/src/Libraries/MusicBrainz/   (props changed)
   trunk/banshee/src/Libraries/MusicBrainz/Makefile.am
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz.mdp
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Artist.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Disc.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/DiscLinux.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/DiscWin32.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Event.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Label.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/LocalDisc.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzEntity.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzException.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzItem.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzObject.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzService.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Query.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Relation.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Release.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Track.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Utils.cs
   trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/XmlRequestEventArgs.cs
Removed:
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDisc.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/build/build.environment.mk
   trunk/banshee/build/pkg-config/banshee-1-core.pc.in
   trunk/banshee/configure.ac
   trunk/banshee/src/Core/Banshee.Core/Resources/translators.xml
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd.mdp
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs
   trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs
   trunk/banshee/src/Extensions/Banshee.AudioCd/Makefile.am
   trunk/banshee/src/Libraries/Libraries.mds
   trunk/banshee/src/Libraries/Makefile.am

Modified: trunk/banshee/build/build.environment.mk
==============================================================================
--- trunk/banshee/build/build.environment.mk	(original)
+++ trunk/banshee/build/build.environment.mk	Sat Mar 29 00:05:08 2008
@@ -78,6 +78,11 @@
 LINK_MONO_MEDIA = -r:$(DIR_BIN)/Mono.Media.dll
 LINK_MONO_MEDIA_DEPS = $(REF_MONO_MEDIA) $(LINK_MONO_MEDIA)
 
+# MusicBrainz
+REF_MUSICBRAINZ = $(LINK_SYSTEM)
+LINK_MUSICBRAINZ = -r:$(DIR_BIN)/MusicBrainz.dll
+LINK_MUSICBRAINZ_DEPS = $(REF_MUSICBRAINZ) $(LINK_MUSICBRAINZ)
+
 #DIR_MUSICBRAINZ = $(DIR_LIBRARIES)/MusicBrainz
 #MONO_BASE_PATH += $(DIR_MUSICBRAINZ)
 #REF_MUSICBRAINZ = $(LINK_SYSTEM)
@@ -120,7 +125,7 @@
 REF_BACKEND_HAL = $(LINK_BANSHEE_SERVICES_DEPS) $(LINK_DBUS)
 
 # Extensions
-REF_EXTENSION_AUDIOCD = $(LINK_BANSHEE_SERVICES_DEPS)
+REF_EXTENSION_AUDIOCD = $(LINK_BANSHEE_SERVICES_DEPS) $(LINK_MUSICBRAINZ_DEPS)
 REF_EXTENSION_BOOKMARKS = $(LINK_BANSHEE_THICKCLIENT_DEPS)
 REF_EXTENSION_MULTIMEDIAKEYS = $(LINK_BANSHEE_SERVICES_DEPS)
 REF_EXTENSION_NOTIFICATIONAREA = $(LINK_BANSHEE_THICKCLIENT_DEPS)

Modified: trunk/banshee/build/pkg-config/banshee-1-core.pc.in
==============================================================================
--- trunk/banshee/build/pkg-config/banshee-1-core.pc.in	(original)
+++ trunk/banshee/build/pkg-config/banshee-1-core.pc.in	Sat Mar 29 00:05:08 2008
@@ -6,5 +6,5 @@
 Name: Banshee Core
 Description: Core APIs for the Banshee Media Framework
 Version: @VERSION@
-Requires: taglib-sharp ndesk-dbus-1.0 ndesk-dbus-glib-1.0 glib-sharp-2.0 mono-addins banshee-1-hyena
+Requires: taglib-sharp ndesk-dbus-1.0 ndesk-dbus-glib-1.0 glib-sharp-2.0 mono-addins banshee-1-hyena banshee-1-musicbrainz
 Libs: -r:${bansheedir}/Banshee.Core.dll

Added: trunk/banshee/build/pkg-config/banshee-1-musicbrainz.pc.in
==============================================================================
--- (empty file)
+++ trunk/banshee/build/pkg-config/banshee-1-musicbrainz.pc.in	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,10 @@
+prefix= prefix@
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+bansheedir=${libdir}/banshee-1
+
+Name: Banshee MusicBrainz
+Description: MusicBrainz libraries for the Banshee Media Framework
+Version: @VERSION@
+Libs: -r:System -r:${bansheedir}/MusicBrainz.dll
+

Modified: trunk/banshee/configure.ac
==============================================================================
--- trunk/banshee/configure.ac	(original)
+++ trunk/banshee/configure.ac	Sat Mar 29 00:05:08 2008
@@ -152,6 +152,7 @@
 src/Libraries/Lastfm/Makefile
 src/Libraries/Lastfm.Gui/Makefile
 src/Libraries/Mono.Media/Makefile
+src/Libraries/MusicBrainz/Makefile
 
 src/Extensions/Makefile
 src/Extensions/Banshee.AudioCd/Makefile

Modified: trunk/banshee/src/Core/Banshee.Core/Resources/translators.xml
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Resources/translators.xml	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Resources/translators.xml	Sat Mar 29 00:05:08 2008
@@ -82,8 +82,8 @@
     <person>Kjartan Maraas</person>
   </language>
   <language code="nl" name="Dutch">
-    <person>Pepijn van de Geer</person>
     <person>Wouter Bolsterlee</person>
+    <person>Pepijn van de Geer</person>
     <person>Stijn Verslycken</person>
   </language>
   <language code="oc" name="Occitan">

Modified: trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd.mdp
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd.mdp	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd.mdp	Sat Mar 29 00:05:08 2008
@@ -11,13 +11,14 @@
     <File name="Banshee.AudioCd.addin.xml" subtype="Code" buildaction="EmbedAsResource" />
     <File name="Banshee.AudioCd/AudioCdService.cs" subtype="Code" buildaction="Compile" />
     <File name="Banshee.AudioCd/AudioCdSource.cs" subtype="Code" buildaction="Compile" />
-    <File name="Banshee.AudioCd/AudioCdDisc.cs" subtype="Code" buildaction="Compile" />
+    <File name="Banshee.AudioCd/AudioCdDiscModel.cs" subtype="Code" buildaction="Compile" />
   </Contents>
   <References>
     <ProjectReference type="Project" localcopy="True" refto="Banshee.Core" />
     <ProjectReference type="Project" localcopy="True" refto="Banshee.Services" />
     <ProjectReference type="Project" localcopy="True" refto="Hyena" />
     <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+    <ProjectReference type="Project" localcopy="True" refto="MusicBrainz" />
   </References>
   <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="True" RelativeMakefileName="./Makefile.am">
     <BuildFilesVar Sync="True" Name="SOURCES" />

Copied: trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs (from r3578, /trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDisc.cs)
==============================================================================
--- /trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDisc.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdDiscModel.cs	Sat Mar 29 00:05:08 2008
@@ -28,17 +28,25 @@
 
 using System;
 
+using MusicBrainz;
 using Banshee.Hardware;
+using Banshee.Collection;
 
 namespace Banshee.AudioCd
 {
-    public class AudioCdDisc
+    public class AudioCdDiscModel : MemoryTrackListModel
     {
         private IDiscVolume volume;
         
-        public AudioCdDisc (IDiscVolume volume)
+        public AudioCdDiscModel (IDiscVolume volume)
         {
             this.volume = volume;
+            ReadDisc ();
+        }
+        
+        private void ReadDisc ()
+        {
+            
         }
         
         public IDiscVolume Volume {

Modified: trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdService.cs	Sat Mar 29 00:05:08 2008
@@ -74,7 +74,7 @@
         {
             lock (this) {
                 if (!sources.ContainsKey (volume.Uuid) && volume.HasAudio) {
-                    AudioCdSource source = new AudioCdSource (this, new AudioCdDisc (volume));
+                    AudioCdSource source = new AudioCdSource (this, new AudioCdDiscModel (volume));
                     sources.Add (volume.Uuid, source);
                     ServiceManager.SourceManager.AddSource (source);
                     Log.DebugFormat ("Mapping audio CD ({0})", volume.Uuid);

Modified: trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Banshee.AudioCd/AudioCdSource.cs	Sat Mar 29 00:05:08 2008
@@ -38,16 +38,13 @@
     public class AudioCdSource : Source, ITrackModelSource, IUnmapableSource, IDisposable
     {
         private AudioCdService service;
-        private AudioCdDisc disc;
-        private MemoryTrackListModel track_model;
+        private AudioCdDiscModel disc_model;
         
-        public AudioCdSource (AudioCdService service, AudioCdDisc disc) 
-            : base (Catalog.GetString ("Audio CD"), disc.Title, 200)
+        public AudioCdSource (AudioCdService service, AudioCdDiscModel discModel) 
+            : base (Catalog.GetString ("Audio CD"), discModel.Title, 200)
         {
             this.service = service;
-            this.disc = disc;
-            
-            track_model = new MemoryTrackListModel ();
+            this.disc_model = discModel;
             
             Properties.SetStringList ("Icon.Name", "media-cdrom", "gnome-dev-cdrom-audio", "source-cd-audio");
             Properties.SetString ("UnmapSourceActionLabel", Catalog.GetString ("Eject Disc"));
@@ -57,8 +54,8 @@
         {
         }
         
-        public AudioCdDisc Disc {
-            get { return disc; }
+        public AudioCdDiscModel DiscModel {
+            get { return disc_model; }
         }
 
 #region Source Overrides
@@ -77,7 +74,7 @@
         }
         
         public override int Count {
-            get { return track_model.Count; }
+            get { return disc_model.Count; }
         }
 
 #endregion
@@ -85,7 +82,7 @@
 #region ITrackModelSource Implementation
 
         public TrackListModel TrackModel {
-            get { return track_model; }
+            get { return disc_model; }
         }
 
         public AlbumListModel AlbumModel {
@@ -98,7 +95,7 @@
 
         public void Reload ()
         {
-            track_model.Reload ();
+            disc_model.Reload ();
         }
 
         public void RemoveSelectedTracks ()
@@ -137,15 +134,15 @@
         {
             System.Threading.ThreadPool.QueueUserWorkItem (delegate {
                 try {
-                    disc.Volume.Unmount ();
-                    disc.Volume.Eject ();
+                    disc_model.Volume.Unmount ();
+                    disc_model.Volume.Eject ();
                 } catch (Exception e) {
                     Log.Error (Catalog.GetString ("Could not eject Audio CD"), e.Message, true);
                     Log.Exception (e);
                 }
             });
             
-            service.UnmapDiscVolume (disc.Volume.Uuid);
+            service.UnmapDiscVolume (disc_model.Volume.Uuid);
             
             return true;
         }

Modified: trunk/banshee/src/Extensions/Banshee.AudioCd/Makefile.am
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.AudioCd/Makefile.am	(original)
+++ trunk/banshee/src/Extensions/Banshee.AudioCd/Makefile.am	Sat Mar 29 00:05:08 2008
@@ -4,7 +4,7 @@
 INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
 
 SOURCES =  \
-	Banshee.AudioCd/AudioCdDisc.cs \
+	Banshee.AudioCd/AudioCdDiscModel.cs \
 	Banshee.AudioCd/AudioCdService.cs \
 	Banshee.AudioCd/AudioCdSource.cs
 

Modified: trunk/banshee/src/Libraries/Libraries.mds
==============================================================================
--- trunk/banshee/src/Libraries/Libraries.mds	(original)
+++ trunk/banshee/src/Libraries/Libraries.mds	Sat Mar 29 00:05:08 2008
@@ -6,14 +6,16 @@
       <Entry build="True" name="Lastfm" configuration="Debug" />
       <Entry build="True" name="Lastfm.Gui" configuration="Debug" />
       <Entry build="True" name="Mono.Media" configuration="Debug" />
+      <Entry build="True" name="MusicBrainz" configuration="Debug" />
     </Configuration>
   </Configurations>
-  <StartMode startupentry="Nereid" single="True">
+  <StartMode startupentry="MusicBrainz" single="True">
     <Execute type="None" entry="Hyena" />
     <Execute type="None" entry="Hyena.Gui" />
     <Execute type="None" entry="Lastfm" />
     <Execute type="None" entry="Lastfm.Gui" />
     <Execute type="None" entry="Mono.Media" />
+    <Execute type="None" entry="MusicBrainz" />
   </StartMode>
   <Entries>
     <Entry filename="Hyena/Hyena.mdp" />
@@ -21,5 +23,6 @@
     <Entry filename="Lastfm/Lastfm.mdp" />
     <Entry filename="Lastfm.Gui/Lastfm.Gui.mdp" />
     <Entry filename="Mono.Media/Mono.Media.mdp" />
+    <Entry filename="MusicBrainz/MusicBrainz.mdp" />
   </Entries>
 </Combine>
\ No newline at end of file

Modified: trunk/banshee/src/Libraries/Makefile.am
==============================================================================
--- trunk/banshee/src/Libraries/Makefile.am	(original)
+++ trunk/banshee/src/Libraries/Makefile.am	Sat Mar 29 00:05:08 2008
@@ -2,6 +2,7 @@
    Hyena \
    Hyena.Gui \
    Mono.Media \
+   MusicBrainz \
    Lastfm \
    Lastfm.Gui
 

Added: trunk/banshee/src/Libraries/MusicBrainz/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/Makefile.am	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,25 @@
+ASSEMBLY = MusicBrainz
+TARGET = library
+LINK = $(REF_MUSICBRAINZ)
+SOURCES =  \
+	MusicBrainz/Artist.cs \
+	MusicBrainz/Disc.cs \
+	MusicBrainz/DiscLinux.cs \
+	MusicBrainz/DiscWin32.cs \
+	MusicBrainz/Event.cs \
+	MusicBrainz/Label.cs \
+	MusicBrainz/LocalDisc.cs \
+	MusicBrainz/MusicBrainzEntity.cs \
+	MusicBrainz/MusicBrainzException.cs \
+	MusicBrainz/MusicBrainzItem.cs \
+	MusicBrainz/MusicBrainzObject.cs \
+	MusicBrainz/MusicBrainzService.cs \
+	MusicBrainz/Query.cs \
+	MusicBrainz/Relation.cs \
+	MusicBrainz/Release.cs \
+	MusicBrainz/Track.cs \
+	MusicBrainz/Utils.cs \
+	MusicBrainz/XmlRequestEventArgs.cs 
+
+include $(top_srcdir)/build/build.mk
+

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz.mdp
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz.mdp	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,43 @@
+<Project name="MusicBrainz" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject">
+  <Configurations active="Debug">
+    <Configuration name="Debug" ctype="DotNetProjectConfiguration">
+      <Output directory="../../../bin" assembly="MusicBrainz" />
+      <Build debugmode="True" target="Library" />
+      <Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" />
+      <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" generatexmldocumentation="False" ctype="CSharpCompilerParameters" />
+    </Configuration>
+  </Configurations>
+  <Contents>
+    <File name="MusicBrainz/Artist.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Disc.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/DiscLinux.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/DiscWin32.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Event.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Label.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/LocalDisc.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/MusicBrainzEntity.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/MusicBrainzException.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/MusicBrainzItem.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/MusicBrainzObject.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/MusicBrainzService.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Query.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Relation.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Release.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Track.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/Utils.cs" subtype="Code" buildaction="Compile" />
+    <File name="MusicBrainz/XmlRequestEventArgs.cs" subtype="Code" buildaction="Compile" />
+  </Contents>
+  <References>
+    <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  </References>
+  <Deployment.LinuxDeployData generateScript="False" />
+  <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="True" RelativeMakefileName="Makefile.am">
+    <BuildFilesVar Sync="True" Name="SOURCES" />
+    <DeployFilesVar />
+    <ResourcesVar />
+    <OthersVar />
+    <GacRefVar />
+    <AsmRefVar />
+    <ProjectRefVar />
+  </MonoDevelop.Autotools.MakefileInfo>
+</Project>
\ No newline at end of file

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Artist.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Artist.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,200 @@
+/***************************************************************************
+ *  Artist.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    public enum ArtistType
+    {
+        Unknown,
+        Group,
+        Person
+    }
+
+    public sealed class ArtistReleaseType
+    {
+        string str;
+
+        public ArtistReleaseType (ReleaseType type, bool various) : this ((Enum)type, various)
+        {
+        }
+
+        public ArtistReleaseType (ReleaseStatus status, bool various) : this ((Enum)status, various)
+        {
+        }
+
+        ArtistReleaseType (Enum enumeration, bool various)
+        {
+            str = various
+                ? "va-" + Utils.EnumToString (enumeration)
+                : "sa-" + Utils.EnumToString (enumeration);
+        }
+
+        public override string ToString ()
+        {
+            return str;
+        }
+
+    }
+
+    public sealed class Artist : MusicBrainzEntity
+    {
+        const string EXTENSION = "artist";
+        
+        protected override string UrlExtension {
+            get { return EXTENSION; }
+        }
+
+        public static ArtistReleaseType DefaultArtistReleaseType =
+            new ArtistReleaseType (ReleaseStatus.Official, false);
+        
+        ArtistReleaseType artist_release_type = DefaultArtistReleaseType;
+        
+        public ArtistReleaseType ArtistReleaseType {
+            get { return artist_release_type; }
+            set {
+                artist_release_type = value;
+                releases = null;
+                have_all_releases = false;
+            }
+        }
+
+        Artist (string mbid) : base (mbid, null)
+        {
+        }
+
+        Artist (string mbid, string parameters) : base (mbid, parameters)
+        {
+        }
+
+        Artist (string mbid, ArtistReleaseType artist_release_type)
+            : this (mbid, "&inc=" + artist_release_type.ToString ())
+        {
+            have_all_releases = true;
+            this.artist_release_type = artist_release_type;
+        }
+
+        internal Artist (XmlReader reader) : base (reader, false)
+        {
+        }
+
+        protected override void HandleCreateInc (StringBuilder builder)
+        {
+            AppendIncParameters (builder, artist_release_type.ToString ());
+            base.HandleCreateInc (builder);
+        }
+
+        protected override void HandleLoadMissingData ()
+        {
+            Artist artist = new Artist (Id, CreateInc ());
+            type = artist.Type;
+            base.HandleLoadMissingData (artist);
+        }
+
+        protected override bool HandleAttributes (XmlReader reader)
+        {
+            switch (reader ["type"]) {
+            case "Group":
+                type = ArtistType.Group;
+                break;
+            case "Person":
+                type = ArtistType.Person;
+                break;
+            }
+            return type != ArtistType.Unknown;
+        }
+
+        protected override bool HandleXml (XmlReader reader)
+        {
+            reader.Read ();
+            bool result = base.HandleXml (reader);
+            if (!result) {
+                result = true;
+                switch (reader.Name) {
+                case "release-list":
+                    if (reader.ReadToDescendant ("release")) {
+                        List<Release> releases = new List<Release> ();
+                        do releases.Add (new Release (reader.ReadSubtree ()));
+                        while (reader.ReadToNextSibling ("release"));
+                        this.releases = releases.AsReadOnly ();
+                    }
+                    break;
+                default:
+                    reader.Skip (); // FIXME this is a workaround for Mono bug 334752
+                    result = false;
+                    break;
+                }
+            }
+            reader.Close ();
+            return result;
+        }
+
+        #region Properties
+
+        [Queryable ("arid")]
+        public override string Id {
+            get { return base.Id; }
+        }
+
+        [Queryable ("artist")]
+        public override string Name {
+            get { return base.Name; }
+        }
+
+        ArtistType? type;
+        [Queryable ("artype")]
+        public ArtistType Type {
+            get { return GetPropertyOrDefault (ref type, ArtistType.Unknown); }
+        }
+
+        ReadOnlyCollection<Release> releases;
+        bool have_all_releases;
+        public ReadOnlyCollection<Release> Releases {
+            get {
+                return releases ?? (have_all_releases
+                    ? releases = new ReadOnlyCollection<Release> (new Release [0])
+                    : new Artist (Id, artist_release_type).Releases);
+            }
+        }
+
+        #endregion
+        
+        #region Static
+
+        public static Artist Get (string mbid)
+        {
+            if (mbid == null) throw new ArgumentNullException ("mbid");
+            return new Artist (mbid);
+        }
+
+        public static Query<Artist> Query (string name)
+        {
+            if (name == null) throw new ArgumentNullException ("name");
+            return new Query<Artist> (EXTENSION, QueryLimit, CreateNameParameter (name));
+        }
+
+        public static Query<Artist> QueryLucene (string luceneQuery)
+        {
+            if (luceneQuery == null) throw new ArgumentNullException ("luceneQuery");
+            return new Query<Artist> (EXTENSION, QueryLimit, CreateLuceneParameter (luceneQuery));
+        }
+
+        public static implicit operator string (Artist artist)
+        {
+            return artist.ToString ();
+        }
+        
+        #endregion
+        
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Disc.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Disc.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,41 @@
+/***************************************************************************
+ *  Disc.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    public class Disc
+    {
+        string id;
+        int sectors;
+
+        internal Disc ()
+        {
+        }
+        
+        internal Disc (XmlReader reader)
+        {
+            reader.Read ();
+            int.TryParse (reader ["sectors"], out sectors);
+            id = reader ["id"];
+            reader.Close ();
+        }
+
+        public string Id {
+            get { return id; }
+            protected set { id = value; }
+        }
+
+        public int Sectors {
+            get { return sectors; }
+            protected set { sectors = value; }
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/DiscLinux.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/DiscLinux.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,150 @@
+/***************************************************************************
+ *  DiscLinux.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+// This is based on $Id: disc_linux.c 8505 2006-09-30 00:02:18Z luks $
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace MusicBrainz
+{
+    internal sealed class DiscLinux : LocalDisc
+    {
+        const int O_RDONLY = 0x0;
+        const int O_NONBLOCK = 0x4000;
+        const int CDROMREADTOCHDR = 0x5305;
+        const int CDROMREADTOCENTRY = 0x5306;
+        const int CDROMMULTISESSION = 0x5310;
+        const int CDROM_LBA = 0x01;
+        const int CDROM_LEADOUT = 0xAA;
+        const int CD_FRAMES = 75;
+        const int XA_INTERVAL = ((60 + 90 + 2) * CD_FRAMES);
+        
+        [DllImport ("libc.so.6")]
+        static extern int open (string path, int flags);
+        
+        [DllImport ("libc.so.6")]
+        static extern int close (int fd);
+        
+        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
+        static extern int read_toc_header (int fd, int request, ref cdrom_tochdr header);
+        static int read_toc_header (int fd, ref cdrom_tochdr header)
+        {
+            return read_toc_header (fd, CDROMREADTOCHDR, ref header);
+        }
+        
+        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
+        static extern int read_multisession (int fd, int request, ref cdrom_multisession multisession);
+        static int read_multisession (int fd, ref cdrom_multisession multisession)
+        {
+            return read_multisession (fd, CDROMMULTISESSION, ref multisession);
+        }
+        
+        [DllImport ("libc.so.6", EntryPoint = "ioctl")]
+        static extern int read_toc_entry (int fd, int request, ref cdrom_tocentry entry);
+        static int read_toc_entry (int fd, ref cdrom_tocentry entry)
+        {
+            return read_toc_entry (fd, CDROMREADTOCENTRY, ref entry);
+        }
+        
+        struct cdrom_tochdr
+        {
+            public byte cdth_trk0;
+            public byte cdth_trk1;
+        }
+        
+        struct cdrom_tocentry
+        {
+            public byte cdte_track;
+            public byte adr_ctrl;
+            public byte cdte_format;
+            public int lba;
+            public byte cdte_datamode;
+        }
+        
+        struct cdrom_multisession
+        {
+            public int lba;
+            public byte xa_flag;
+            public byte addr_format;
+        }
+        
+        int ReadTocHeader (int fd)
+        {
+            cdrom_tochdr th = new cdrom_tochdr ();
+            cdrom_multisession ms = new cdrom_multisession ();
+            
+            int ret = read_toc_header (fd, ref th);
+            
+            if (ret < 0) return ret;
+            
+            FirstTrack = th.cdth_trk0;
+            LastTrack = th.cdth_trk1;
+            
+            ms.addr_format = CDROM_LBA;
+            ret = read_multisession (fd, ref ms);
+            
+            if(ms.xa_flag != 0) LastTrack--;
+            
+            return ret;
+        }
+        
+        int ReadTocEntry (int fd, byte track_number, ref ulong lba)
+        {
+            cdrom_tocentry te = new cdrom_tocentry ();
+            te.cdte_track = track_number;
+            te.cdte_format = CDROM_LBA;
+            
+            int ret = read_toc_entry (fd, ref te);
+            
+            if(ret == 0) lba = (ulong)te.lba;
+            
+            return ret;
+        }
+        
+        int ReadLeadout (int fd, ref ulong lba)
+        {
+            cdrom_multisession ms = new cdrom_multisession ();
+            ms.addr_format = CDROM_LBA;
+            
+            int ret = read_multisession (fd, ref ms);
+            
+            if (ms.xa_flag != 0) {
+                lba = (ulong)(ms.lba - XA_INTERVAL);
+                return ret;
+            }
+            
+            return ReadTocEntry (fd, CDROM_LEADOUT, ref lba);
+        }
+        
+        internal DiscLinux (string device)
+        {
+            int fd = open (device, O_RDONLY | O_NONBLOCK);
+            
+            if (fd < 0) throw new Exception (String.Format ("Cannot open device '{0}'", device));
+            
+            try {
+                if (ReadTocHeader(fd) < 0) throw new Exception ("Cannot read table of contents");
+                if (LastTrack == 0) throw new Exception ("This disc has no tracks");
+                
+                ulong lba = 0;
+                ReadLeadout (fd, ref lba);
+                TrackOffsets [0] = (int)lba + 150;
+                
+                for (byte i = FirstTrack; i <= LastTrack; i++) {
+                    ReadTocEntry (fd, i, ref lba);
+                    TrackOffsets[i] = (int)lba + 150;
+                }
+            } finally {
+                close (fd);
+            }
+            
+            Init ();
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/DiscWin32.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/DiscWin32.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,108 @@
+/***************************************************************************
+ *  DiscWin32.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+// This is based on $Id: disc_win32.c 8506 2006-09-30 19:02:57Z luks $
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace MusicBrainz
+{
+    internal sealed class DiscWin32 : LocalDisc
+    {
+        [DllImport ("winmm")]
+        static extern Int32 mciSendString (String command,
+                                           StringBuilder buffer,
+                                           Int32 bufferSize,
+                                           IntPtr hwndCallback);
+
+        [DllImport ("winmm")]
+        static extern Int32 mciGetErrorString (Int32 errorCode,
+                                               StringBuilder errorText,
+                                               Int32 errorTextSize);
+        
+        delegate void MciCall (string result);
+
+        internal DiscWin32 (string device)
+        {
+            string device_string = device.Length == 0
+                ? "cdaudio" : string.Format ("{0} type cdaudio", device);
+
+            string alias = string.Format ("musicbrainz_cdio_{0}_{1}",
+                Environment.TickCount, Thread.CurrentThread.ManagedThreadId);
+
+            MciClosure (
+                "sysinfo cdaudio quantity wait",
+                "Could not get the list of CD audio devices",
+                delegate (string result) {
+                    if (int.Parse (result.ToString ()) <= 0)
+                        throw new Exception ("No CD audio devices present.");
+                });
+
+            MciClosure (
+                string.Format ("open {0} shareable alias {1} wait", device_string, alias),
+                string.Format ("Could not open device {0}", device),
+                null);
+
+            MciClosure (
+                string.Format ("status {0} number of tracks wait", alias),
+                "Could not read number of tracks",
+                delegate (string result) {
+                    FirstTrack = 1;
+                    LastTrack = byte.Parse (result);
+                });
+
+            MciClosure (
+                string.Format ("set {0} time format msf wait", alias),
+                "Could not set time format",
+                null);
+
+            for (int i = 1; i <= LastTrack; i++)
+                MciClosure (
+                    string.Format ("status {0} position track {1} wait", alias, i),
+                    string.Format ("Could not get position for track {0}", i),
+                    delegate (string result) {
+                        TrackOffsets [i] =
+                            int.Parse (result.Substring (0,2)) * 4500 +
+                            int.Parse (result.Substring (3,2)) * 75 +
+                            int.Parse (result.Substring (6,2));
+                    });
+
+            MciClosure (
+                string.Format ("status {0} length track {1} wait", alias, LastTrack),
+                "Could not read the length of the last track",
+                delegate (string result) {
+                    TrackOffsets [0] =
+                        int.Parse (result.Substring (0, 2)) * 4500 +
+                        int.Parse (result.Substring (3, 2)) * 75 +
+                        int.Parse (result.Substring (6, 2)) +
+                        TrackOffsets [LastTrack] + 1;
+                });
+
+            MciClosure (
+                string.Format ("close {0} wait", alias),
+                string.Format ("Could not close device {0}", device),
+                null);
+            
+            Init ();
+        }
+
+        static StringBuilder mci_result = new StringBuilder (128);
+        static StringBuilder mci_error = new StringBuilder (256);
+        static void MciClosure (string command, string failure_message, MciCall code)
+        {
+            int ret = mciSendString (command, mci_result, mci_result.Capacity, IntPtr.Zero);
+            if (ret != 0) {
+                mciGetErrorString (ret, mci_error, mci_error.Capacity);
+                throw new Exception (string.Format ("{0} : {1}", failure_message, mci_error.ToString ()));
+            } else if (code != null) code (mci_result.ToString ());
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Event.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Event.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,62 @@
+/***************************************************************************
+ *  Event.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    public sealed class Event
+    {
+        string date;
+        string country;
+        string catalog_number;
+        string barcode;
+        Label label;
+        ReleaseFormat format = ReleaseFormat.None;
+
+        internal Event (XmlReader reader)
+        {
+            reader.Read ();
+            date = reader ["date"];
+            country = reader ["country"];
+            catalog_number = reader ["catalog-number"];
+            barcode = reader ["barcode"];
+            format = Utils.StringToEnum<ReleaseFormat> (reader ["format"]);
+                if (reader.ReadToDescendant ("label")) {
+                    label = new Label (reader.ReadSubtree ());
+                    reader.Read (); // FIXME this is a workaround for Mono bug 334752
+                }
+            reader.Close ();
+        }
+
+        public string Date {
+            get { return date; }
+        }
+
+        public string Country {
+            get { return country; }
+        }
+
+        public string CatalogNumber {
+            get { return catalog_number; }
+        }
+
+        public string Barcode {
+            get { return barcode; }
+        }
+
+        public Label Label {
+            get { return label; }
+        }
+
+        public ReleaseFormat Format {
+            get { return format; }
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Label.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Label.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,112 @@
+/***************************************************************************
+ *  Label.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Text;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    public enum LabelType
+    {
+        Unspecified,
+        Distributor,
+        Holding,
+        OriginalProduction,
+        BootlegProduction,
+        ReissueProduction
+    }
+
+    public sealed class Label : MusicBrainzEntity
+    {
+        const string EXTENSION = "label";
+        
+        protected override string UrlExtension {
+            get { return EXTENSION; }
+        }
+
+        Label (string mbid) : base (mbid, null)
+        {
+        }
+
+        Label (string mbid, string parameters) : base (mbid, parameters)
+        {
+        }
+
+        internal Label (XmlReader reader) : base (reader, false)
+        {
+        }
+
+        protected override void HandleLoadMissingData ()
+        {
+            Label label = new Label (Id, CreateInc ());
+            type = label.Type;
+            base.HandleLoadMissingData (label);
+        }
+
+        protected override bool HandleAttributes (XmlReader reader)
+        {
+            type = Utils.StringToEnum<LabelType> (reader ["type"]);
+            return this.type != null;
+        }
+
+        protected override bool HandleXml (XmlReader reader)
+        {
+            reader.Read ();
+            bool result = base.HandleXml (reader);
+            if (!result) {
+                if (reader.Name == "country") {
+                    result = true;
+                    reader.Read ();
+                    if (reader.NodeType == XmlNodeType.Text)
+                        country = reader.ReadContentAsString ();
+                } else reader.Skip (); // FIXME this is a workaround for Mono bug 334752
+            }
+            reader.Close ();
+            return result;
+        }
+
+        string country;
+        public string Country {
+            get { return GetPropertyOrNull (ref country); }
+        }
+
+        LabelType? type;
+        public LabelType Type {
+            get { return GetPropertyOrDefault (ref type, LabelType.Unspecified); }
+        }
+        
+        #region Static
+
+        public static Label Get (string mbid)
+        {
+            if (mbid == null) throw new ArgumentNullException ("mbid");
+            return new Label (mbid);
+        }
+
+        public static Query<Label> Query (string name)
+        {
+            if (name == null) throw new ArgumentNullException ("name");
+            return new Query<Label> (EXTENSION, QueryLimit, CreateNameParameter (name));
+        }
+
+        public static Query<Label> QueryLucene (string luceneQuery)
+        {
+            if (luceneQuery == null) throw new ArgumentNullException ("luceneQuery");
+            return new Query<Label> (EXTENSION, QueryLimit, CreateLuceneParameter (luceneQuery));
+        }
+
+        public static implicit operator string (Label label)
+        {
+            return label.ToString ();
+        }
+        
+        #endregion
+
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/LocalDisc.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/LocalDisc.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,113 @@
+/***************************************************************************
+ *  LocalDisc.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace MusicBrainz
+{
+    public abstract class LocalDisc : Disc
+    {
+        byte first_track;
+        byte last_track;
+        int [] track_durations;
+        int [] track_offsets = new int [100];
+        
+        internal LocalDisc()
+        {
+        }
+        
+        protected void Init ()
+        {
+            track_durations = new int [last_track];
+            for (int i = 1; i <= last_track; i++) {
+                track_durations [i - 1] = i < last_track
+                    ? track_offsets [i + 1] - track_offsets [i]
+                    : track_offsets [0] - track_offsets [i];
+                track_durations [i - 1] /= 75; // 75 frames in a second
+            }
+            GenerateId ();
+        }
+        
+        void GenerateId ()
+        {
+            StringBuilder input_builder = new StringBuilder (804);
+            
+            input_builder.Append (string.Format ("{0:X2}", FirstTrack));
+            input_builder.Append (string.Format ("{0:X2}", LastTrack));
+            
+            for (int i = 0; i < track_offsets.Length; i++)
+                input_builder.Append (string.Format ("{0:X8}", track_offsets [i]));
+
+            // MB uses a slightly modified RFC822 for reasons of URL happiness.
+            string base64 = Convert.ToBase64String (SHA1.Create ()
+                .ComputeHash (Encoding.ASCII.GetBytes (input_builder.ToString ())));
+            StringBuilder hash_builder = new StringBuilder (base64.Length);
+            
+            foreach (char c in base64)
+                if      (c == '+')  hash_builder.Append ('.');
+                else if (c == '/')  hash_builder.Append ('_');
+                else if (c == '=')  hash_builder.Append ('-');
+                else                hash_builder.Append (c);
+            
+            Id = hash_builder.ToString ();
+        }
+        
+        protected byte FirstTrack {
+            get { return first_track; }
+            set { first_track = value; }
+        }
+
+        protected byte LastTrack {
+            get { return last_track; }
+            set { last_track = value; }
+        }
+
+        protected int [] TrackOffsets {
+            get { return track_offsets; }
+        }
+        
+        public int [] TrackDurations {
+            get { return track_durations; }
+        }
+        
+        string submission_url;
+        public string SubmissionUrl {
+            get {
+                if (submission_url == null) {
+                    StringBuilder builder = new StringBuilder ();
+                    builder.Append ("http://mm.musicbrainz.org/bare/cdlookup.html";);
+                    builder.Append ("?id=");
+                    builder.Append (Id);
+                    builder.Append ("&tracks=");
+                    builder.Append (last_track);
+                    builder.Append ("&toc=");
+                    builder.Append (first_track);
+                    builder.Append ('+');
+                    builder.Append (last_track);
+                    builder.Append ('+');
+                    builder.Append (track_offsets [0]);
+                    for (int i = first_track; i <= last_track; i++) {
+                        builder.Append ('+');
+                        builder.Append (track_offsets [i]);
+                    }
+                    submission_url = builder.ToString ();
+                }
+                return submission_url;
+            }
+        }
+        
+        public static LocalDisc GetFromDevice (string device)
+        {
+            if (device == null) throw new ArgumentNullException ("device");
+            return Environment.OSVersion.Platform != PlatformID.Unix
+                ? (LocalDisc)new DiscWin32 (device) : new DiscLinux (device);
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzEntity.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzEntity.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,135 @@
+/***************************************************************************
+ *  MusicBrainzEntity.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    // A person-like entity, such as an artist or a label.
+    public abstract class MusicBrainzEntity : MusicBrainzObject
+    {
+        internal MusicBrainzEntity (string mbid, string parameters) : base (mbid, parameters)
+        {
+        }
+
+        internal MusicBrainzEntity (XmlReader reader, bool all_rels_loaded) : base (reader, all_rels_loaded)
+        {
+        }
+
+        protected override void HandleCreateInc (StringBuilder builder)
+        {
+            if (aliases == null) AppendIncParameters (builder, "aliases");
+            base.HandleCreateInc (builder);
+        }
+
+        protected void HandleLoadMissingData (MusicBrainzEntity entity)
+        {
+            name = entity.Name;
+            sort_name = entity.SortName;
+            disambiguation = entity.Disambiguation;
+            begin_date = entity.BeginDate;
+            end_date = entity.EndDate;
+            if (aliases == null) aliases = entity.Aliases;
+            base.HandleLoadMissingData (entity);
+        }
+
+        protected override bool HandleXml (XmlReader reader)
+        {
+            bool result = true;
+            switch (reader.Name) {
+            case "name":
+                reader.Read ();
+                if (reader.NodeType == XmlNodeType.Text)
+                    name = reader.ReadContentAsString ();
+                break;
+            case "sort-name":
+                reader.Read ();
+                if (reader.NodeType == XmlNodeType.Text)
+                    sort_name = reader.ReadContentAsString ();
+                break;
+            case "disambiguation":
+                reader.Read ();
+                if (reader.NodeType == XmlNodeType.Text)
+                    disambiguation = reader.ReadContentAsString ();
+                break;
+            case "life-span":
+                begin_date = reader ["begin"];
+                end_date = reader ["end"];
+                break;
+            case "alias-list":
+                if (reader.ReadToDescendant ("alias")) {
+                    List<string> aliases = new List<string> ();
+                    do {
+                        reader.Read ();
+                        if (reader.NodeType == XmlNodeType.Text)
+                            aliases.Add (reader.ReadContentAsString ());
+                    } while (reader.ReadToNextSibling ("alias"));
+                    this.aliases = aliases.AsReadOnly ();
+                }
+                break;
+            default:
+                result = false;
+                break;
+            }
+            return result;
+        }
+
+        # region Properties
+
+        string name;
+        public virtual string Name {
+            get { return GetPropertyOrNull (ref name); }
+        }
+
+        string sort_name;
+        [Queryable]
+        public virtual string SortName {
+            get { return GetPropertyOrNull (ref sort_name); }
+        }
+
+        string disambiguation;
+        [Queryable ("comment")]
+        public virtual string Disambiguation {
+            get { return GetPropertyOrNull (ref disambiguation); }
+        }
+
+        string begin_date;
+        [Queryable ("begin")]
+        public virtual string BeginDate {
+            get { return GetPropertyOrNull (ref begin_date); }
+        }
+
+        string end_date;
+        [Queryable ("end")]
+        public virtual string EndDate {
+            get { return GetPropertyOrNull (ref end_date); }
+        }
+
+        ReadOnlyCollection<string> aliases;
+        [QueryableMember ("Contains", "alias")]
+        public virtual ReadOnlyCollection<string> Aliases {
+            get { return GetPropertyOrNew (ref aliases); }
+        }
+
+        #endregion
+
+        protected static string CreateNameParameter (string name)
+        {
+            return "&name=" + Utils.PercentEncode (name);
+        }
+
+        public override string ToString ()
+        {
+            return name;
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzException.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzException.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,36 @@
+/***************************************************************************
+ *  MusicBrainzException.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+
+namespace MusicBrainz
+{
+    public sealed class MusicBrainzInvalidParameterException : Exception
+    {
+        public MusicBrainzInvalidParameterException ()
+            : base ("One of the parameters is invalid. The MBID may be invalid, or you may be using an illegal parameter for this resource type.")
+        {
+        }
+    }
+
+    public sealed class MusicBrainzNotFoundException : Exception
+    {
+        public MusicBrainzNotFoundException ()
+            : base ("Specified resource was not found. Perhaps it was merged or deleted.")
+        {
+        }
+    }
+
+    public sealed class MusicBrainzUnauthorizedException : Exception
+    {
+        public MusicBrainzUnauthorizedException ()
+            : base ("The client is not authorized to perform this action. You may not have authenticated, or the username or password may be incorrect.")
+        {
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzItem.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzItem.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,144 @@
+/***************************************************************************
+ *  MusicBrainzItem.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Text;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    public abstract class ItemQueryParameters
+    {
+        internal ItemQueryParameters ()
+        {
+        }
+        
+        string title;
+        public string Title {
+            get { return title; }
+            set { title = value; }
+        }
+
+        string artist;
+        public string Artist {
+            get { return artist; }
+            set { artist = value; }
+        }
+
+        string artist_id;
+        public string ArtistId {
+            get { return artist_id; }
+            set { artist_id = value; }
+        }
+
+        ReleaseType? release_type;
+        public ReleaseType? ReleaseType {
+            get { return release_type; }
+            set { release_type = value; }
+        }
+
+        ReleaseStatus? release_status;
+        public ReleaseStatus? ReleaseStatus {
+            get { return release_status; }
+            set { release_status = value; }
+        }
+
+        int? count;
+        public int? TrackCount {
+            get { return count; }
+            set { count = value; }
+        }
+
+        protected void AppendBaseToBuilder (StringBuilder builder)
+        {
+            if (title != null) {
+                builder.Append ("&title=");
+                Utils.PercentEncode (builder, title);
+            }
+            if (artist != null) {
+                builder.Append ("&artist=");
+                Utils.PercentEncode (builder, artist);
+            }
+            if (artist_id != null) {
+                builder.Append ("&artistid=");
+                builder.Append (artist_id);
+            }
+            if (release_type != null) {
+                builder.Append ("&releasetypes=");
+                builder.Append (Utils.EnumToString (release_type.Value));
+            }
+            if (release_status != null) {
+                builder.Append (release_type != null ? "+" : "&releasetypes=");
+                builder.Append (release_status);
+            }
+            if (count != null) {
+                builder.Append ("&count=");
+                builder.Append (count.Value);
+            }
+        }
+    }
+
+    // The item-like product of an artist, such as a track or a release.
+    public abstract class MusicBrainzItem : MusicBrainzObject
+    {
+        internal MusicBrainzItem (string mbid, string parameters) : base (mbid, parameters)
+        {
+        }
+
+        internal MusicBrainzItem (XmlReader reader, bool all_rels_loaded) : base (reader, all_rels_loaded)
+        {
+        }
+
+        protected override void HandleCreateInc (StringBuilder builder)
+        {
+            if (artist == null) AppendIncParameters(builder, "artist");
+            base.HandleCreateInc (builder);
+        }
+
+        protected void HandleLoadMissingData (MusicBrainzItem item)
+        {
+            title = item.Title;
+            if (artist == null) artist = item.Artist;
+            base.HandleLoadMissingData (item);
+        }
+
+        protected override bool HandleXml (XmlReader reader)
+        {
+            bool result = true;
+            switch (reader.Name) {
+            case "title":
+                reader.Read ();
+                if (reader.NodeType == XmlNodeType.Text)
+                    title = reader.ReadContentAsString ();
+                break;
+            case "artist":
+                artist = new Artist (reader.ReadSubtree ());
+                break;
+            default:
+                result = false;
+                break;
+            }
+            return result;
+        }
+
+        string title;
+        public virtual string Title {
+            get { return GetPropertyOrNull (ref title); }
+        }
+
+        Artist artist;
+        [Queryable ("artist")]
+        public virtual Artist Artist {
+            get { return GetPropertyOrNull (ref artist); }
+        }
+
+        public override string ToString () {
+            return title;
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzObject.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzObject.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,421 @@
+/***************************************************************************
+ *  MusicBrainzObject.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    internal delegate void XmlProcessingDelegate (XmlReader reader);
+
+    public abstract class MusicBrainzObject
+    {
+        static TimeSpan min_interval = new TimeSpan (0, 0, 1); // 1 second
+        static DateTime last_accessed;
+        static readonly object server_mutex = new object ();
+
+        bool all_data_loaded;
+        protected bool AllDataLoaded {
+            get { return all_data_loaded; }
+        }
+
+        bool all_rels_loaded;
+        protected bool AllRelsLoaded {
+            get { return all_rels_loaded; }
+            set { all_rels_loaded = value; }
+        }
+
+        protected abstract string UrlExtension { get; }
+
+        internal MusicBrainzObject (string mbid, string parameters)
+        {
+            all_data_loaded = true;
+            CreateFromMbid (mbid, parameters ?? CreateInc ());
+        }
+
+        internal MusicBrainzObject (XmlReader reader, bool all_rels_loaded)
+        {
+            this.all_rels_loaded = all_rels_loaded;
+            CreateFromXml (reader);
+        }
+
+        protected string CreateInc ()
+        {
+            StringBuilder builder = new StringBuilder ();
+            HandleCreateInc (builder);
+            return builder.ToString ();
+        }
+        
+        static string [] rels_params = new string [] {
+            "artist-rels",
+            "release-rels",
+            "track-rels",
+            "label-rels",
+            "url-rels"
+        };
+
+        protected virtual void HandleCreateInc (StringBuilder builder)
+        {
+            if (!all_rels_loaded)
+                AppendIncParameters (builder, rels_params);
+        }
+        
+        protected void AppendIncParameters (StringBuilder builder, string parameter)
+        {
+            builder.Append (builder.Length == 0 ? "&inc=" : "+");
+            builder.Append (parameter);
+        }
+        
+        protected void AppendIncParameters (StringBuilder builder, string parameter1, string parameter2)
+        {
+            builder.Append (builder.Length == 0 ? "&inc=" : "+");
+            builder.Append (parameter1);
+            builder.Append ('+');
+            builder.Append (parameter2);
+        }
+
+        protected void AppendIncParameters (StringBuilder builder, string [] parameters)
+        {
+            foreach (string parameter in parameters)
+                AppendIncParameters (builder, parameter);
+        }
+
+        void CreateFromMbid (string mbid, string parameters)
+        {
+            XmlProcessingClosure (
+                CreateUrl (UrlExtension, mbid, parameters),
+                delegate (XmlReader reader) {
+                    reader.ReadToFollowing ("metadata");
+                    reader.Read ();
+                    CreateFromXml (reader.ReadSubtree ());
+                    reader.Close ();
+                }
+            );
+        }
+
+        protected abstract bool HandleAttributes (XmlReader reader);
+        protected abstract bool HandleXml (XmlReader reader);
+        void CreateFromXml (XmlReader reader)
+        {
+            reader.Read ();
+            id = reader ["id"];
+            byte.TryParse (reader ["ext:score"], out score);
+            HandleAttributes (reader);
+            while (reader.Read () && reader.NodeType != XmlNodeType.EndElement) {
+                if (reader.Name == "relation-list") {
+                    all_rels_loaded = true;
+                    switch (reader ["target-type"]) {
+                    case "Artist":
+                        List<Relation<Artist>> artist_rels = new List<Relation<Artist>> ();
+                        CreateRelation (reader.ReadSubtree (), artist_rels);
+                        this.artist_rels = artist_rels.AsReadOnly ();
+                        break;
+                    case "Release":
+                        List<Relation<Release>> release_rels = new List<Relation<Release>> ();
+                        CreateRelation (reader.ReadSubtree (), release_rels);
+                        this.release_rels = release_rels.AsReadOnly ();
+                        break;
+                    case "Track":
+                        List<Relation<Track>> track_rels = new List<Relation<Track>> ();
+                        CreateRelation (reader.ReadSubtree (), track_rels);
+                        this.track_rels = track_rels.AsReadOnly ();
+                        break;
+                    case "Label":
+                        List<Relation<Label>> label_rels = new List<Relation<Label>> ();
+                        CreateRelation (reader.ReadSubtree (), label_rels);
+                        this.label_rels = label_rels.AsReadOnly ();
+                        break;
+                    case "Url":
+                        if (!reader.ReadToDescendant ("relation")) break;
+                        List<UrlRelation> url_rels = new List<UrlRelation> ();
+                        do {
+                            RelationDirection direction = RelationDirection.Forward;
+                            string direction_string = reader ["direction"];
+                            if (direction_string != null && direction_string == "backward")
+                                direction = RelationDirection.Backward;
+                            string attributes_string = reader ["attributes"];
+                            string [] attributes = attributes_string == null
+                                ? null : attributes_string.Split (' ');
+                            url_rels.Add (new UrlRelation (
+                                reader ["type"],
+                                reader ["target"],
+                                direction,
+                                reader ["begin"],
+                                reader ["end"],
+                                attributes));
+                        } while (reader.ReadToNextSibling ("relation"));
+                        this.url_rels = url_rels.AsReadOnly ();
+                        break;
+                    }
+                } else
+                    HandleXml (reader.ReadSubtree ());
+			}
+            reader.Close ();
+		}
+
+        protected void LoadMissingData ()
+        {
+            if (!all_data_loaded) {
+                HandleLoadMissingData ();
+                all_data_loaded = true;
+            }
+        }
+
+        protected abstract void HandleLoadMissingData ();
+        protected void HandleLoadMissingData (MusicBrainzObject obj)
+        {
+            if (!all_rels_loaded) {
+                artist_rels = obj.ArtistRelations;
+                release_rels = obj.ReleaseRelations;
+                track_rels = obj.TrackRelations;
+                label_rels = obj.LabelRelations;
+                url_rels = obj.UrlRelations;
+            }
+        }
+
+        #region Properties
+        
+        protected T GetPropertyOrNull<T> (ref T field_reference) where T : class
+        {
+            if (field_reference == null) LoadMissingData ();
+            return field_reference;
+        }
+        
+        protected T GetPropertyOrDefault<T> (ref T? field_reference, T default_value) where T : struct
+        {
+            if (field_reference == null) LoadMissingData ();
+            return field_reference ?? default_value;
+        }
+        
+        protected ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference)
+        {
+            return GetPropertyOrNew (ref field_reference, true);
+        }
+        
+        protected ReadOnlyCollection<T> GetPropertyOrNew<T> (ref ReadOnlyCollection<T> field_reference, bool condition)
+        {
+            if (field_reference == null && condition) LoadMissingData ();
+            return field_reference ?? new ReadOnlyCollection<T> (new T [0]);
+        }
+
+        string id;
+        public virtual string Id {
+            get { return id; }
+        }
+
+        byte score;
+        public virtual byte Score {
+            get { return score; }
+        }
+
+        ReadOnlyCollection<Relation<Artist>> artist_rels;
+        public virtual ReadOnlyCollection<Relation<Artist>> ArtistRelations {
+            get { return GetPropertyOrNew (ref artist_rels, !all_rels_loaded); }
+        }
+
+        ReadOnlyCollection<Relation<Release>> release_rels;
+        public virtual ReadOnlyCollection<Relation<Release>> ReleaseRelations {
+            get { return GetPropertyOrNew (ref release_rels, !all_rels_loaded); }
+        }
+
+        ReadOnlyCollection<Relation<Track>> track_rels;
+        public virtual ReadOnlyCollection<Relation<Track>> TrackRelations {
+            get { return GetPropertyOrNew (ref track_rels, !all_rels_loaded); }
+        }
+
+        ReadOnlyCollection<Relation<Label>> label_rels;
+        public virtual ReadOnlyCollection<Relation<Label>> LabelRelations {
+            get { return GetPropertyOrNew (ref label_rels, !all_rels_loaded); }
+        }
+
+        ReadOnlyCollection<UrlRelation> url_rels;
+        public virtual ReadOnlyCollection<UrlRelation> UrlRelations {
+            get { return GetPropertyOrNew (ref url_rels, !all_rels_loaded); }
+        }
+
+        public override bool Equals (object obj)
+        {
+            MusicBrainzObject mbobj = obj as MusicBrainzObject;
+            return mbobj != null && mbobj.GetType ().Equals (GetType ()) && mbobj.Id == Id;
+        }
+
+        public override int GetHashCode ()
+        {
+            return (GetType ().Name + Id).GetHashCode ();
+        }
+
+        #endregion
+
+        #region Static
+
+        static void CreateRelation<T> (XmlReader reader, List<Relation<T>> relations) where T : MusicBrainzObject
+        {
+            while (reader.ReadToFollowing ("relation")) {
+                string type = reader ["type"];
+                RelationDirection direction = RelationDirection.Forward;
+                string direction_string = reader ["direction"];
+                if (direction_string != null && direction_string == "backward")
+                    direction = RelationDirection.Backward;
+                string begin = reader ["begin"];
+                string end = reader ["end"];
+                string attributes_string = reader ["attributes"];
+                string [] attributes = attributes_string == null
+                    ? null : attributes_string.Split (' ');
+
+                reader.Read ();
+                relations.Add (new Relation<T> (
+                    type,
+                    ConstructMusicBrainzObjectFromXml<T> (reader.ReadSubtree ()),
+                    direction,
+                    begin,
+                    end,
+                    attributes));
+            }
+            reader.Close ();
+        }
+
+        static string CreateUrl (string url_extension, int limit, int offset, string parameters)
+        {
+            StringBuilder builder = new StringBuilder ();
+            if (limit != 25) {
+                builder.Append ("&limit=");
+                builder.Append (limit);
+            }
+            if (offset != 0) {
+                builder.Append ("&offset=");
+                builder.Append (offset);
+            }
+            builder.Append (parameters);
+            return CreateUrl (url_extension, string.Empty, builder.ToString ());
+        }
+
+        static string CreateUrl (string url_extension, string mbid, string parameters)
+        {
+            StringBuilder builder = new StringBuilder (
+                MusicBrainzService.ProviderUrl.Length + mbid.Length + parameters.Length + 9);
+            builder.Append (MusicBrainzService.ProviderUrl);
+            builder.Append (url_extension);
+            builder.Append ('/');
+            builder.Append (mbid);
+            builder.Append ("?type=xml");
+            builder.Append (parameters);
+            return builder.ToString ();
+        }
+
+        static void XmlProcessingClosure (string url, XmlProcessingDelegate code)
+        {
+            Monitor.Enter (server_mutex);
+
+            // Don't access the MB server twice within a second
+            TimeSpan time = DateTime.Now - last_accessed;
+            if (min_interval > time)
+                Thread.Sleep ((min_interval - time).Milliseconds);
+
+            HttpWebRequest request = WebRequest.Create (url) as HttpWebRequest;
+            bool cache_implemented = false;
+            
+            try {
+                request.CachePolicy = MusicBrainzService.CachePolicy;
+                cache_implemented = true;
+            } catch (NotImplementedException) {
+            }
+            
+            HttpWebResponse response = null;
+            
+            try {
+                response = request.GetResponse () as HttpWebResponse;
+            } catch (WebException e) {
+                response = (HttpWebResponse)e.Response;
+            }
+            
+            if (response == null) throw new MusicBrainzNotFoundException ();
+
+            switch (response.StatusCode) {
+            case HttpStatusCode.BadRequest:
+                Monitor.Exit (server_mutex);
+                throw new MusicBrainzInvalidParameterException ();
+            case HttpStatusCode.Unauthorized:
+                Monitor.Exit (server_mutex);
+                throw new MusicBrainzUnauthorizedException ();
+            case HttpStatusCode.NotFound:
+                Monitor.Exit (server_mutex);
+                throw new MusicBrainzNotFoundException ();
+            }
+
+            bool from_cache = cache_implemented && response.IsFromCache;
+
+            MusicBrainzService.OnXmlRequest (url, from_cache);
+
+            if (from_cache) Monitor.Exit (server_mutex);
+
+            // Should we read the stream into a memory stream and run the XmlReader off of that?
+            code (new XmlTextReader (response.GetResponseStream ()));
+            response.Close ();
+
+            if (!from_cache) {
+                last_accessed = DateTime.Now;
+                Monitor.Exit (server_mutex);
+            }
+        }
+
+        #endregion
+
+        #region Query
+
+        protected static byte QueryLimit {
+            get { return 100; }
+        }
+
+        protected static string CreateLuceneParameter (string query)
+        {
+            return "&query=" + Utils.PercentEncode (query);
+        }
+
+        internal static List<T> Query<T> (string url_extension,
+                                          byte limit, int offset,
+                                          string parameters,
+                                          out int? count) where T : MusicBrainzObject
+        {
+            int count_value = 0;
+            List<T> results = new List<T> ();
+            XmlProcessingClosure (
+                CreateUrl (url_extension, limit, offset, parameters),
+                delegate (XmlReader reader) {
+                    reader.ReadToFollowing ("metadata");
+                    reader.Read ();
+                    int.TryParse (reader ["count"], out count_value);
+                    while (reader.Read () && reader.NodeType == XmlNodeType.Element)
+                        results.Add (ConstructMusicBrainzObjectFromXml<T> (reader.ReadSubtree ()));
+                    reader.Close ();
+                }
+            );
+            count = count_value == 0 ? results.Count : count_value;
+            return results;
+        }
+
+        static T ConstructMusicBrainzObjectFromXml<T> (XmlReader reader) where T : MusicBrainzObject
+        {
+            ConstructorInfo constructor = typeof (T).GetConstructor (
+                BindingFlags.NonPublic | BindingFlags.Instance,
+                null,
+                new Type [] { typeof (XmlReader) },
+                null);
+            return (T)constructor.Invoke (new object [] {reader});
+        }
+
+        #endregion
+
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzService.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/MusicBrainzService.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,26 @@
+/***************************************************************************
+ *  MusicBrainzService.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Net.Cache;
+
+namespace MusicBrainz
+{
+    public static class MusicBrainzService
+    {
+        public static string ProviderUrl = @"http://musicbrainz.org/ws/1/";;
+        public static RequestCachePolicy CachePolicy;
+        public static event EventHandler<XmlRequestEventArgs> XmlRequest;
+        
+        internal static void OnXmlRequest (string url, bool fromCache)
+        {
+            EventHandler<XmlRequestEventArgs> handler = XmlRequest;
+            if (handler != null) handler (null, new XmlRequestEventArgs (url, fromCache));
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Query.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Query.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,171 @@
+/***************************************************************************
+ *  Query.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MusicBrainz
+{
+    public sealed class Query<T> : IEnumerable<T> where T : MusicBrainzObject
+    {
+        string parameters;
+        string url_extension;
+        byte limit;
+
+        internal Query (string url_extension, byte limit, string parameters)
+        {
+            this.url_extension = url_extension;
+            this.limit = limit;
+            this.parameters = parameters;
+        }
+        
+        List<T> results;
+        List<T> ResultsWindow {
+            get {
+                if (results == null)
+                    results = MusicBrainzObject.Query<T> (url_extension, limit, offset, parameters, out count);
+                return results;
+            }
+        }
+
+        int offset;
+        Dictionary<int, WeakReference> weak_references = new Dictionary<int, WeakReference> ();
+        int Offset {
+            get { return offset; }
+            set {
+                if (value == offset) return;
+                // We WeakReference the results from previous offsets just in case.
+                if (results != null)
+                    if (!weak_references.ContainsKey (offset))
+                        weak_references.Add (offset, new WeakReference (results));
+                    else weak_references [offset].Target = results;
+                results = null;
+                offset = value;
+                if (weak_references.ContainsKey (offset)) {
+                    WeakReference weak_reference = weak_references [offset];
+                    if (weak_reference.IsAlive)
+                        results = weak_reference.Target as List<T>;
+                }
+            }
+        }
+
+        int? count;
+        public int Count {
+            get {
+                if(count == null && ResultsWindow == null) { } // just accessing ResultsWindow will give count a value
+                return count.Value;
+            }
+        }
+
+        public T this [int i] {
+            get {
+                if (i < 0 || i >= Count) throw new IndexOutOfRangeException ();
+                if (i <= offset || i >= offset + limit) 
+                    Offset = i;
+                return ResultsWindow [i - offset];
+            }
+        }
+
+        public List<T> ToList ()
+        {
+            return ToList (0);
+        }
+        
+        public List<T> ToList (int score_threshold)
+        {
+            List<T> list = new List<T> (score_threshold == 0 ? Count : 0);
+            foreach (T result in Best(score_threshold)) list.Add (result);
+            return list;
+        }
+        
+        public T [] ToArray ()
+        {
+            T [] array = new T [Count];
+            for(int i = 0; i < Count; i++) array [i] = this [i];
+            return array;
+        }
+
+        public IEnumerator<T> GetEnumerator ()
+        {
+            for (int i = 0; i < Count; i++) yield return this [i];
+        }
+        
+        IEnumerator IEnumerable.GetEnumerator ()
+        {
+            return GetEnumerator ();
+        }
+        
+        public IEnumerable<T> Best ()
+        {
+            return Best (100);
+        }
+        
+        public IEnumerable<T> Best (int score_threshold)
+        {
+            foreach (T result in this) {
+                if (result.Score < score_threshold) yield break;
+                yield return result;
+            }
+        }
+        
+        public T PerfectMatch ()
+        {
+            byte tmp_limit = limit;
+            limit = 2;
+            T result1 = Count > 0 ? this [0] : null;
+            T result2 = Count > 1 ? this [1] : null;
+            limit = tmp_limit;
+            
+            return (result1 != null && result1.Score == 100 && (result2 == null || result2.Score < 100))
+                ? result1 : null;
+        }
+        
+        public T First ()
+        {
+            byte tmp_limit = limit;
+            limit = 1;
+            T result = Count > 0 ? this [0] : null;
+            limit = tmp_limit;
+            return result;
+        }
+        
+        public static implicit operator T (Query<T> query)
+        {
+            return query.First ();
+        }
+    }
+
+    [AttributeUsage (AttributeTargets.Property)]
+    internal sealed class QueryableAttribute : Attribute
+    {
+        public readonly string Name;
+        
+        public QueryableAttribute ()
+        {
+        }
+        
+        public QueryableAttribute (string name)
+        {
+            Name = name;
+        }
+    }
+
+    [AttributeUsage (AttributeTargets.Property)]
+    internal sealed class QueryableMemberAttribute : Attribute
+    {
+        public readonly string Name;
+        public readonly string Member;
+        public QueryableMemberAttribute (string member, string name)
+        {
+            Member = member;
+            Name = name;
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Relation.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Relation.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,89 @@
+/***************************************************************************
+ *  Relation.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+
+namespace MusicBrainz
+{
+    public enum RelationDirection
+    {
+        Forward,
+        Backward
+    }
+    
+    public abstract class RelationPrimative<T>
+    {
+        T target;
+        string type;
+        string[] attributes;
+        RelationDirection direction;
+        string begin;
+        string end;
+
+        internal RelationPrimative (string type, T target, RelationDirection direction,
+            string begin, string end, string[] attributes)
+        {
+            this.type = type;
+            this.target = target;
+            this.direction = direction;
+            this.begin = begin;
+            this.end = end;
+            this.attributes = attributes;
+        }
+
+        public T Target {
+            get { return target; }
+        }
+
+        public string Type {
+            get { return type; }
+        }
+
+        public string [] Attributes {
+            get { return attributes; }
+        }
+
+        public RelationDirection Direction {
+            get { return direction; }
+        }
+
+        public string BeginDate {
+            get { return begin; }
+        }
+        
+        public string EndDate {
+            get { return end; }
+        }
+    }
+    
+    public sealed class Relation<T> : RelationPrimative<T> where T : MusicBrainzObject
+    {
+        internal Relation (string type,
+                           T target,
+                           RelationDirection direction,
+                           string begin,
+                           string end,
+                           string [] attributes)
+            : base (type, target, direction, begin, end, attributes)
+        {
+        }
+    }
+
+    public sealed class UrlRelation : RelationPrimative<string>
+    {
+        internal UrlRelation(string type,
+                             string target,
+                             RelationDirection direction,
+                             string begin,
+                             string end,
+                             string [] attributes)
+            : base (type, target, direction, begin, end, attributes)
+        {
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Release.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Release.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,364 @@
+/***************************************************************************
+ *  Release.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    # region Enums
+
+    public enum ReleaseType
+    {
+        None,
+        Album,
+        Single,
+        EP,
+        Compilation,
+        Soundtrack,
+        Spokenword,
+        Interview,
+        Audiobook,
+        Live,
+        Remix,
+        Other
+    }
+
+    public enum ReleaseStatus
+    {
+        None,
+        Official,
+        Promotion,
+        Bootleg,
+        PsudoRelease
+    }
+
+    public enum ReleaseFormat
+    {
+        None,
+        Cartridge,
+        Cassette,
+        CD,
+        DAT,
+        Digital,
+        DualDisc,
+        DVD,
+        LaserDisc,
+        MiniDisc,
+        Other,
+        ReelToReel,
+        SACD,
+        Vinyl
+    }
+
+    #endregion
+
+    public sealed class ReleaseQueryParameters : ItemQueryParameters
+    {
+        string disc_id;
+        public string DiscID {
+            get { return disc_id; }
+            set { disc_id = value; }
+        }
+
+        string date;
+        public string Date {
+            get { return date; }
+            set { date = value; }
+        }
+
+        string asin;
+        public string Asin {
+            get { return asin; }
+            set { asin = value; }
+        }
+
+        string language;
+        public string Language {
+            get { return language; }
+            set { language = value; }
+        }
+
+        string script;
+        public string Script {
+            get { return script; }
+            set { script = value; }
+        }
+
+        public override string ToString ()
+        {
+            StringBuilder builder = new StringBuilder ();
+            if (disc_id != null) {
+                builder.Append ("&discid=");
+                builder.Append (disc_id);
+            }
+            if (date != null) {
+                builder.Append ("&date=");
+                Utils.PercentEncode (builder, date);
+            }
+            if (asin != null) {
+                builder.Append ("&asin=");
+                builder.Append (asin);
+            }
+            if (language != null) {
+                builder.Append ("&lang=");
+                builder.Append (language);
+            }
+            if (script != null) {
+                builder.Append ("&script=");
+                builder.Append (script);
+            }
+            AppendBaseToBuilder (builder);
+            return builder.ToString ();
+        }
+    }
+
+    public sealed class Release : MusicBrainzItem
+    {
+        const string EXTENSION = "release";
+        
+        protected override string UrlExtension {
+            get { return EXTENSION; }
+        }
+
+        Release (string mbid) : base (mbid, null)
+        {
+        }
+
+        Release (string mbid, string parameters) : base (mbid, parameters)
+        {
+        }
+
+        internal Release (XmlReader reader) : base(reader, false)
+        {
+        }
+        
+        protected override void HandleCreateInc (StringBuilder builder)
+        {
+            AppendIncParameters (builder, "release-events", "labels");
+            if (discs == null) AppendIncParameters (builder, "discs");
+            if (tracks == null) {
+                AppendIncParameters (builder, "tracks", "track-level-rels");
+                AllRelsLoaded = false;
+            }
+            base.HandleCreateInc (builder);
+        }
+
+        protected override void HandleLoadMissingData ()
+        {
+            Release release = new Release (Id, CreateInc ());
+            type = release.Type;
+            status = release.Status;
+            language = release.Language;
+            script = release.Script;
+            asin = release.Asin;
+            events = release.Events;
+            if (discs == null) discs = release.Discs;
+            if (tracks == null) tracks = release.Tracks;
+            base.HandleLoadMissingData (release);
+        }
+
+        protected override bool HandleAttributes (XmlReader reader)
+        {
+            // How sure am I about getting the type and status in the "Type Status" format?
+            // MB really ought to specify these two things seperatly.
+            string type_string = reader ["type"];
+            if (type_string != null)
+                foreach (string token in type_string.Split (' ')) {
+                    if (type == null) {
+                        type = Utils.StringToEnumOrNull<ReleaseType> (token);
+                        if (type != null) continue;
+                    }
+                    this.status = Utils.StringToEnumOrNull<ReleaseStatus> (token);
+                }
+            return this.type != null || this.status != null;
+        }
+
+        protected override bool HandleXml (XmlReader reader)
+        {
+            reader.Read ();
+            bool result = base.HandleXml (reader);
+            if (!result) {
+                result = true;
+                switch (reader.Name) {
+                case "text-representation":
+                    language = reader ["language"];
+                    script = reader ["script"];
+                    break;
+                case "asin":
+                    reader.Read ();
+                    if (reader.NodeType == XmlNodeType.Text)
+                        asin = reader.ReadContentAsString ();
+                    break;
+                case "disc-list": {
+                    if (reader.ReadToDescendant ("disc")) {
+                        List<Disc> discs = new List<Disc> ();
+                        do discs.Add (new Disc (reader.ReadSubtree ()));
+                        while (reader.ReadToNextSibling ("disc"));
+                        this.discs = discs.AsReadOnly ();
+                    }
+                    break;
+                }
+                case "release-event-list":
+                    if (!AllDataLoaded) reader.Skip(); // FIXME this is a workaround for Mono bug 334752
+                    if (reader.ReadToDescendant ("event")) {
+                        List<Event> events = new List<Event> ();
+                        do events.Add (new Event (reader.ReadSubtree ()));
+                        while (reader.ReadToNextSibling ("event"));
+                        this.events = events.AsReadOnly ();
+                    }
+                    break;
+                case "track-list": {
+                    string offset = reader ["offset"];
+                    if (offset != null)
+                        track_number = int.Parse (offset) + 1;
+                    if (reader.ReadToDescendant ("track")) {
+                        List<Track> tracks = new List<Track> ();
+                        do tracks.Add (new Track (reader.ReadSubtree (), AllDataLoaded));
+                        while (reader.ReadToNextSibling ("track"));
+                        this.tracks = tracks.AsReadOnly ();
+                    }
+                    break;
+                }
+                default:
+                    reader.Skip (); // FIXME this is a workaround for Mono bug 334752
+                    result = false;
+                    break;
+                }
+            }
+            reader.Close ();
+            return result;
+        }
+
+        #region Properties
+
+        [Queryable ("reid")]
+        public override string Id {
+            get { return base.Id; }
+        }
+
+        [Queryable ("release")]
+        public override string Title {
+            get { return base.Title; }
+        }
+
+        ReleaseType? type;
+        [Queryable]
+        public ReleaseType Type {
+            get { return GetPropertyOrDefault (ref type, ReleaseType.None); }
+        }
+
+        ReleaseStatus? status;
+        [Queryable]
+        public ReleaseStatus Status {
+            get { return GetPropertyOrDefault (ref status, ReleaseStatus.None); }
+
+        }
+
+        string language;
+        public string Language {
+            get { return GetPropertyOrNull (ref language); }
+        }
+
+        string script;
+        [Queryable]
+        public string Script {
+            get { return GetPropertyOrNull (ref script); }
+        }
+
+        string asin;
+        [Queryable]
+        public string Asin {
+            get { return GetPropertyOrNull (ref asin); }
+        }
+
+        ReadOnlyCollection<Disc> discs;
+        [QueryableMember("Count", "discids")]
+        public ReadOnlyCollection<Disc> Discs {
+            get { return GetPropertyOrNew (ref discs); }
+        }
+
+        ReadOnlyCollection<Event> events;
+        public ReadOnlyCollection<Event> Events {
+            get { return GetPropertyOrNew (ref events); }
+        }
+
+        ReadOnlyCollection<Track> tracks;
+        [QueryableMember ("Count", "tracks")]
+        public ReadOnlyCollection<Track> Tracks {
+            get { return GetPropertyOrNew (ref tracks); }
+        }
+
+        int? track_number;
+        internal int TrackNumber {
+            get { return track_number != null ? track_number.Value : -1; }
+        }
+
+        #endregion
+        
+        #region Static
+
+        public static Release Get (string mbid)
+        {
+            if (mbid == null) throw new ArgumentNullException ("mbid");
+            return new Release (mbid);
+        }
+
+        public static Query<Release> Query (string title)
+        {
+            if (title == null) throw new ArgumentNullException ("title");
+            
+            ReleaseQueryParameters parameters = new ReleaseQueryParameters ();
+            parameters.Title = title;
+            return Query (parameters);
+        }
+
+        public static Query<Release> Query (string title, string artist)
+        {
+            if (title == null) throw new ArgumentNullException ("title");
+            if (artist == null) throw new ArgumentNullException ("artist");
+            
+            ReleaseQueryParameters parameters = new ReleaseQueryParameters ();
+            parameters.Title = title;
+            parameters.Artist = artist;
+            return Query (parameters);
+        }
+
+        public static Query<Release> Query (ReleaseQueryParameters parameters)
+        {
+            if (parameters == null) throw new ArgumentNullException ("parameters");
+            return new Query<Release> (EXTENSION, QueryLimit, parameters.ToString ());
+        }
+
+        public static Query<Release> QueryFromDevice(string device)
+        {
+            if (device == null) throw new ArgumentNullException ("device");
+            
+            ReleaseQueryParameters parameters = new ReleaseQueryParameters ();
+            parameters.DiscID = LocalDisc.GetFromDevice (device).Id;
+            return Query (parameters);
+        }
+
+        public static Query<Release> QueryLucene (string luceneQuery)
+        {
+            if (luceneQuery == null) throw new ArgumentNullException ("luceneQuery");
+            return new Query<Release> (EXTENSION, QueryLimit, CreateLuceneParameter (luceneQuery));
+        }
+
+        public static implicit operator string (Release release)
+        {
+            return release.ToString ();
+        }
+        
+        #endregion
+
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Track.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Track.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,263 @@
+/***************************************************************************
+ *  Track.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Xml;
+
+namespace MusicBrainz
+{
+    public sealed class TrackQueryParameters : ItemQueryParameters
+    {
+        string release;
+        public string Release {
+            get { return release; }
+            set { release = value; }
+        }
+
+        string release_id;
+        public string ReleaseId {
+            get { return release_id; }
+            set { release_id = value; }
+        }
+
+        uint? duration;
+        public uint? Duration {
+            get { return duration; }
+            set { duration = value; }
+        }
+
+        int? track_number;
+        public int? TrackNumber {
+            get { return track_number; }
+            set { track_number = value; }
+        }
+
+        string puid;
+        public string Puid {
+            get { return puid; }
+            set { puid = value; }
+        }
+
+        public override string ToString ()
+        {
+            StringBuilder builder = new StringBuilder ();
+            if (release != null) {
+                builder.Append ("&release=");
+                Utils.PercentEncode (builder, release);
+            }
+            if (release_id != null) {
+                builder.Append ("&releaseid=");
+                builder.Append (release_id);
+            }
+            if (duration != null) {
+                builder.Append ("&duration=");
+                builder.Append (duration.Value);
+            }
+            if (track_number != null) {
+                builder.Append ("&tracknumber=");
+                builder.Append (track_number.Value);
+            }
+            if (puid != null) {
+                builder.Append ("&puid=");
+                builder.Append (puid);
+            }
+            AppendBaseToBuilder (builder);
+            return builder.ToString ();
+        }
+    }
+
+    public sealed class Track : MusicBrainzItem
+    {
+        const string EXTENSION = "track";
+        
+        protected override string UrlExtension {
+            get { return EXTENSION; }
+        }
+
+        Track (string mbid) : base (mbid, null)
+        {
+        }
+
+        Track (string mbid, string parameters) : base (mbid, parameters)
+        {
+        }
+
+        internal Track (XmlReader reader) : base (reader, false)
+        {
+        }
+
+        internal Track (XmlReader reader, bool all_rels_loaded) : base (reader, all_rels_loaded)
+        {
+        }
+
+        protected override void HandleCreateInc (StringBuilder builder)
+        {
+            if (releases == null) AppendIncParameters (builder, "releases");
+            if (puids == null) AppendIncParameters (builder, "puids");
+            base.HandleCreateInc (builder);
+        }
+
+        protected override void HandleLoadMissingData ()
+        {
+            Track track = new Track (Id, CreateInc ());
+            duration = track.Duration;
+            if (releases == null) releases = track.Releases;
+            if (puids == null) puids = track.Puids;
+            base.HandleLoadMissingData (track);
+        }
+
+        protected override bool HandleAttributes (XmlReader reader)
+        {
+            return true;
+        }
+
+        protected override bool HandleXml (XmlReader reader)
+        {
+            reader.Read ();
+            bool result = base.HandleXml (reader);
+            if (!result) {
+                result = true;
+                switch (reader.Name) {
+                case "duration":
+                    reader.Read ();
+                    if (reader.NodeType == XmlNodeType.Text)
+                        duration = uint.Parse (reader.ReadContentAsString ());
+                    break;
+                case "release-list":
+                    if(reader.ReadToDescendant ("release")) {
+                        List<Release> releases = new List<Release> ();
+                        do releases.Add (new Release (reader.ReadSubtree ()));
+                        while (reader.ReadToNextSibling ("release"));
+                        this.releases = releases.AsReadOnly ();
+                    }
+                    break;
+                case "puid-list":
+                    if(reader.ReadToDescendant ("puid")) {
+                        List<string> puids = new List<string> ();
+                        do puids.Add (reader ["id"]);
+                        while (reader.ReadToNextSibling ("puid"));
+                        this.puids = puids.AsReadOnly ();
+                    }
+                    break;
+                default:
+                    reader.Skip (); // FIXME this is a workaround for Mono bug 334752
+                    result = false;
+                    break;
+                }
+            }
+            reader.Close ();
+            return result;
+        }
+
+        #region Properties
+
+        [Queryable ("trid")]
+        public override string Id {
+            get { return base.Id; }
+        }
+
+        [Queryable ("track")]
+        public override string Title {
+            get { return base.Title; }
+        }
+
+        uint duration;
+        [Queryable ("dur")]
+        public uint Duration {
+            get { return duration; }
+        }
+
+        ReadOnlyCollection<Release> releases;
+        [QueryableMember ("Contains", "release")]
+        public ReadOnlyCollection<Release> Releases {
+            get { return GetPropertyOrNew (ref releases); }
+
+        }
+
+        ReadOnlyCollection<string> puids;
+        public ReadOnlyCollection<string> Puids {
+            get { return GetPropertyOrNew (ref puids); }
+        }
+
+        public int GetTrackNumber (Release release)
+        {
+            if (release == null) throw new ArgumentNullException ("release");
+            
+            foreach (Release r in Releases)
+                if (r.Equals (release))
+                    return r.TrackNumber;
+            return -1;
+        }
+
+        #endregion
+        
+        #region Static
+
+        public static Track Get (string mbid)
+        {
+            if (mbid == null) throw new ArgumentNullException ("mbid");
+            return new Track (mbid);
+        }
+
+        public static Query<Track> Query (string title)
+        {
+            if (title == null) throw new ArgumentNullException ("title");
+            
+            TrackQueryParameters parameters = new TrackQueryParameters ();
+            parameters.Title = title;
+            return Query (parameters);
+        }
+
+        public static Query<Track> Query (string title, string release)
+        {
+            if (title == null) throw new ArgumentNullException ("title");
+            if (release == null) throw new ArgumentNullException ("release");
+            
+            TrackQueryParameters parameters = new TrackQueryParameters ();
+            parameters.Title = title;
+            parameters.Release = release;
+            return Query (parameters);
+        }
+        
+        public static Query<Track> Query (string title, string release, string artist)
+        {
+            if (title == null) throw new ArgumentNullException ("title");
+            if (release == null) throw new ArgumentNullException ("release");
+            if (artist == null) throw new ArgumentNullException ("artist");
+            
+            TrackQueryParameters parameters = new TrackQueryParameters ();
+            parameters.Title = title;
+            parameters.Release = release;
+            parameters.Artist = artist;
+            return Query (parameters);
+        }
+
+        public static Query<Track> Query (TrackQueryParameters parameters)
+        {
+            if (parameters == null) throw new ArgumentNullException ("parameters");
+            return new Query<Track> (EXTENSION, QueryLimit, parameters.ToString ());
+        }
+
+        public static Query<Track> QueryLucene (string luceneQuery)
+        {
+            if(luceneQuery == null) throw new ArgumentNullException ("luceneQuery"); 
+            return new Query<Track> (EXTENSION, QueryLimit, CreateLuceneParameter (luceneQuery));
+        }
+
+        public static implicit operator string (Track track)
+        {
+            return track.ToString ();
+        }
+        
+        #endregion
+
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Utils.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/Utils.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,64 @@
+/***************************************************************************
+ *  Utils.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+using System.Text;
+
+namespace MusicBrainz
+{
+    internal static class Utils
+    {
+        public static string EnumToString (Enum enumeration)
+        {
+            string str = enumeration.ToString ();
+            StringBuilder builder = new StringBuilder (str.Length);
+            builder.Append (str [0]);
+            for (int i = 1; i < str.Length; i++) {
+                if (str [i] >= 'A' && str [i] <= 'Z')
+                    builder.Append ('-'); 
+                builder.Append (str [i]);
+            }
+            return builder.ToString ();
+        }
+        
+        public static T StringToEnum<T> (string name) where T : struct
+        {
+            return StringToEnumOrNull<T> (name) ?? default (T);
+        }
+        
+        public static T? StringToEnumOrNull<T> (string name) where T : struct
+        {
+            if (name != null)
+                foreach (T value in Enum.GetValues (typeof (T)))
+                    if (Enum.GetName (typeof (T), value) == name)
+                        return value;
+            return null;
+        }
+        
+        public static string PercentEncode (string value)
+        {
+            StringBuilder builder = new StringBuilder ();
+            PercentEncode (builder, value);
+            return builder.ToString ();
+        }
+        
+        public static void PercentEncode (StringBuilder builder, string value)
+        {
+            foreach (char c in value) {
+                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || 
+                    c == '-' || c == '_' || c == '.' || c == '~')
+                    builder.Append (c);
+                else {
+                    builder.Append ('%');
+                    foreach (byte b in Encoding.UTF8.GetBytes (new char [] { c }))
+                        builder.Append (string.Format ("{0:X}", b));
+                } 
+            }
+        }
+    }
+}

Added: trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/XmlRequestEventArgs.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Libraries/MusicBrainz/MusicBrainz/XmlRequestEventArgs.cs	Sat Mar 29 00:05:08 2008
@@ -0,0 +1,24 @@
+/***************************************************************************
+ *  XmlRequestEventArgs.cs
+ *
+ *  Authored by Scott Peterson <lunchtimemama gmail com>
+ * 
+ *  The author disclaims copyright to this source code.
+ ****************************************************************************/
+
+using System;
+
+namespace MusicBrainz
+{
+    public sealed class XmlRequestEventArgs : EventArgs
+    {
+        public readonly string Uri;
+        public readonly bool FromCache;
+        
+        public XmlRequestEventArgs(string uri, bool fromCache)
+        {
+            Uri = uri;
+            FromCache = fromCache;
+        }
+    }
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]