diff --git a/src/Core/Banshee.Core/Banshee.Base/CoverArtSpec.cs b/src/Core/Banshee.Core/Banshee.Base/CoverArtSpec.cs
index 5d09b82..54f924a 100644
--- a/src/Core/Banshee.Core/Banshee.Base/CoverArtSpec.cs
+++ b/src/Core/Banshee.Core/Banshee.Base/CoverArtSpec.cs
@@ -41,6 +41,11 @@ namespace Banshee.Base
         private static string unknown_album_tr = Catalog.GetString ("Unknown Album");
         private static string unknown_album = "Unknown Album";
 
+        public static readonly int[] CoverSizes = new int[] { 36, 40, 48, 64, 300 };
+        public static readonly Banshee.IO.ExtensionSet WhiteListImageFileExtensions = new Banshee.IO.ExtensionSet (
+            "jpg", "jpeg", "png", "gif", "bmp"
+        );
+
         public static bool CoverExists (string artist, string album)
         {
             return CoverExists (CreateArtistAlbumId (artist, album));
@@ -112,5 +117,25 @@ namespace Banshee.Base
         public static string RootPath {
             get { return root_path; }
         }
+
+        public static void RemoveCoverArt (string aaid)
+        {
+            string filename = GetPath (aaid);
+            SafeUri safe_uri = new SafeUri (filename);
+            
+            if (Banshee.IO.File.Exists (safe_uri)) {
+                Hyena.Log.DebugFormat ("Removing original cover art {0}", filename);
+                Banshee.IO.File.Delete (safe_uri);
+            }
+
+            foreach (int size in CoverSizes) {
+                filename = GetPathForSize (aaid, size);
+                safe_uri = new SafeUri (filename);
+                if (Banshee.IO.File.Exists (safe_uri)) {
+                    Hyena.Log.DebugFormat ("Removing cover art {0}", filename);
+                    Banshee.IO.File.Delete (safe_uri);
+                }
+            }
+        }
     }
 }
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/CoverArtPage.cs b/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/CoverArtPage.cs
new file mode 100644
index 0000000..aaa5b6f
--- /dev/null
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/CoverArtPage.cs
@@ -0,0 +1,239 @@
+//
+// CoverArtPage.cs
+//
+// Author:
+//   Jensen Somers <jensen.somers@gmail.com>
+//
+// Copyright (C) 2009 Jensen Somers
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using Mono.Unix;
+using Gtk;
+
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.Collection.Gui;
+using Banshee.ServiceStack;
+
+namespace Banshee.Gui.TrackEditor
+{
+    public class CoverArtPage : FieldPage, ITrackEditorPage
+    {
+        private Frame cover_frame;
+        private Image cover_image;
+        
+        private HBox box;
+        private VBox left;
+        private VBox right;
+        
+        private Button add_cover;
+        private Button remove_cover;
+        private Button replace_cover;
+        
+        private EditorTrackInfo current_track;
+        
+        public int Order {
+            get { return 50; }
+        }
+
+        public string Title {
+            get { return Catalog.GetString("Cover Art"); }
+        }
+
+        public override void LoadTrack (EditorTrackInfo track)
+        {
+            base.LoadTrack (track);
+        }
+
+        protected override void AddFields ()
+        {
+            InitializeGui ();
+            InitializeButtons ();
+
+            AddField (left, cover_frame,
+                Catalog.GetString("Cover art"),
+                delegate { return String.Empty; },
+                delegate (EditorTrackInfo track, Widget widget) { ReadCoverArt (track); },
+                delegate (EditorTrackInfo track, Widget widget) { ReadCoverArt (track); }, 
+                FieldOptions.NoSync);
+
+            AddField (right, add_cover,
+                Catalog.GetString ("Add cover"),
+                delegate { return String.Empty; },
+                delegate (EditorTrackInfo track, Widget widget) {},
+                delegate (EditorTrackInfo track, Widget widget) {},
+                FieldOptions.NoSync);
+
+            AddField (right, replace_cover,
+                Catalog.GetString ("Replace cover"),
+                delegate { return String.Empty; },
+                delegate (EditorTrackInfo track, Widget widget) {},
+                delegate (EditorTrackInfo track, Widget widget) {},
+                FieldOptions.NoSync);
+
+            AddField (right, remove_cover,
+                Catalog.GetString ("Remove cover"),
+                delegate { return String.Empty; },
+                delegate (EditorTrackInfo track, Widget widget) {},
+                delegate (EditorTrackInfo track, Widget widget) {},
+                FieldOptions.NoSync);
+        }
+
+        private void InitializeGui ()
+        {
+            // Initialize image.
+            cover_frame = new Frame ();
+            cover_image = new Image ();
+
+            cover_image.IconName = "media-optical";
+            cover_image.PixelSize = 300;
+            cover_frame.Add (cover_image);
+
+            // Initialize box.
+            box = new HBox ();
+            left = EditorUtilities.CreateVBox ();
+            right = EditorUtilities.CreateVBox ();
+
+            box.PackStart (left, true, true, 0);
+            box.PackStart (new VSeparator (), false, false, 12);
+            box.PackStart (right, false, false, 0);
+            box.ShowAll ();
+
+            PackStart (box, false, false, 0);
+        }
+
+        private void InitializeButtons ()
+        {
+            add_cover = new Button (Stock.Add);
+            add_cover.UseStock = true;
+            add_cover.Show ();
+            add_cover.Clicked += AddCoverArt;
+
+            remove_cover = new Button (Stock.Remove);
+            remove_cover.UseStock = true;
+            remove_cover.Show ();
+            remove_cover.Clicked += RemoveCoverArt;
+
+            replace_cover = new Button (Stock.Edit);
+            replace_cover.UseStock = true;
+            replace_cover.Show ();
+            replace_cover.Clicked += EditCoverArt;
+        }
+
+        private void RemoveCoverArt (object sender, EventArgs e)
+        {
+            CoverArtSpec.RemoveCoverArt (current_track.ArtworkId);
+            ReadCoverArt (current_track);  
+        }
+
+        private void EditCoverArt (object sender, EventArgs e)
+        {
+            Banshee.Gui.Dialogs.FileChooserDialog image_chooser = new Banshee.Gui.Dialogs.FileChooserDialog (
+                Catalog.GetString ("Select cover art"),
+                FileChooserAction.Open);
+            image_chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
+            image_chooser.AddButton (Stock.Open, ResponseType.Ok);
+            image_chooser.AddFilter (Hyena.Gui.GtkUtilities.GetFileFilter (Catalog.GetString ("Image Files"), CoverArtSpec.WhiteListImageFileExtensions.List));
+            image_chooser.SelectMultiple = false;
+            image_chooser.DefaultResponse = ResponseType.Ok;
+
+            if (image_chooser.Run () == (int)ResponseType.Ok) {
+                SaveCoverArt (image_chooser.Filename, true);
+            }
+
+            image_chooser.Destroy ();
+
+            ReadCoverArt (current_track);
+        }
+
+        private void AddCoverArt (object sender, EventArgs e)
+        {
+            Banshee.Gui.Dialogs.FileChooserDialog image_chooser = new Banshee.Gui.Dialogs.FileChooserDialog (
+                Catalog.GetString ("Select cover art"),
+                FileChooserAction.Open);
+            image_chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
+            image_chooser.AddButton (Stock.Open, ResponseType.Ok);
+            image_chooser.AddFilter (Hyena.Gui.GtkUtilities.GetFileFilter (Catalog.GetString ("Image Files"), CoverArtSpec.WhiteListImageFileExtensions.List));
+            image_chooser.SelectMultiple = false;
+            image_chooser.DefaultResponse = ResponseType.Ok;
+
+            if (image_chooser.Run () == (int)ResponseType.Ok) {
+                SaveCoverArt (image_chooser.Filename, false);
+            }
+
+            image_chooser.Destroy ();
+
+            ReadCoverArt (current_track);
+        }
+
+        private void ReadCoverArt (EditorTrackInfo track)
+        {
+            ArtworkManager artwork = ServiceManager.Get<ArtworkManager> ();
+            cover_image.Pixbuf = artwork.LookupScalePixbuf (track.ArtworkId, 300);
+
+            cover_frame.ShadowType = ShadowType.None;
+            if (cover_image.Pixbuf == null) {
+                cover_image.IconName = "media-optical";
+                cover_image.PixelSize = 300;
+                
+                remove_cover.State = StateType.Insensitive;
+                replace_cover.State = StateType.Insensitive;
+            } else {
+                add_cover.State = StateType.Insensitive;
+            }
+
+            current_track = track;
+        }
+
+        private void SaveCoverArt (string filename, bool edit)
+        {
+            if (edit) {
+                // Remove previously existing cover art.
+                CoverArtSpec.RemoveCoverArt (current_track.ArtworkId);
+            }
+            
+            string cover_filename = CoverArtSpec.GetPath (current_track.ArtworkId);
+            FileStream file_stream = File.OpenRead (filename);
+
+            if (String.IsNullOrEmpty (cover_filename) || file_stream == null || !file_stream.CanRead) {
+                return;
+            }
+
+            SafeUri path_uri = new SafeUri (cover_filename);
+            if (Banshee.IO.File.Exists (path_uri)) {
+                return;
+            }
+            
+            // Save the file to a temporary path while downloading/copying,
+            // so that nobody sees it and thinks it's ready for use before it is
+            SafeUri tmp_uri = new SafeUri (String.Format ("{0}.part", cover_filename));
+            try {
+                Banshee.IO.StreamAssist.Save (file_stream, Banshee.IO.File.OpenWrite (tmp_uri, true));
+                Banshee.IO.File.Move (tmp_uri, path_uri);
+            } catch (Exception e) {
+                Hyena.Log.Exception (e);
+            }
+        }
+    }
+}
diff --git a/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml b/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
index daabd16..ea28283 100644
--- a/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
+++ b/src/Core/Banshee.ThickClient/Banshee.ThickClient.addin.xml
@@ -28,6 +28,7 @@
     <!--<TrackEditorPage class="Banshee.Gui.TrackEditor.LyricsPage"/>-->
     <TrackEditorPage class="Banshee.Gui.TrackEditor.StatisticsPage"/>
     <!--<TrackEditorPage class="Banshee.Gui.TrackEditor.HelpPage"/>-->
+    <TrackEditorPage class="Banshee.Gui.TrackEditor.CoverArtPage"/>
   </Extension>
   
   <!-- Exported Extension Points -->
diff --git a/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj b/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
index 4893796..ab66eea 100644
--- a/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
+++ b/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
@@ -276,6 +274,7 @@
     <Compile Include="Banshee.ContextPane\BaseContextPage.cs" />
     <Compile Include="Banshee.ContextPane\ContextPageManager.cs" />
     <Compile Include="Banshee.ContextPane\ContextPane.cs" />
+    <Compile Include="Banshee.Gui.TrackEditor\CoverArtPage.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
\ No newline at end of file
diff --git a/src/Core/Banshee.ThickClient/Makefile.am b/src/Core/Banshee.ThickClient/Makefile.am
index d53e769..ee3af07 100644
--- a/src/Core/Banshee.ThickClient/Makefile.am
+++ b/src/Core/Banshee.ThickClient/Makefile.am
@@ -57,6 +57,7 @@ SOURCES =  \
 	Banshee.Gui.DragDrop/DragDropUtilities.cs \
 	Banshee.Gui.TrackEditor/AlbumArtistEntry.cs \
 	Banshee.Gui.TrackEditor/BasicTrackDetailsPage.cs \
+	Banshee.Gui.TrackEditor/CoverArtPage.cs \
 	Banshee.Gui.TrackEditor/EditorEditableUndoAdapter.cs \
 	Banshee.Gui.TrackEditor/EditorMode.cs \
 	Banshee.Gui.TrackEditor/EditorTrackInfo.cs \
