[banshee] Make save-to-file and move-on-info-save jobs restartable



commit efbab18f147eac2595dae9e6613bcfdfa75657f3
Author: Gabriel Burt <gabriel burt gmail com>
Date:   Thu Apr 16 21:11:36 2009 -0500

    Make save-to-file and move-on-info-save jobs restartable
    
    2009-04-16  Gabriel Burt  <gabriel burt gmail com>
    
    	* src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs:
    	* src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs:
    	* src/Core/Banshee.Core/Banshee.Streaming/SaveTrackMetadataJob.cs:
    	* src/Core/Banshee.Core/Makefile.am: Remove old save job and move its
    	save-to-file logic into StreamTagger.
    
    	* src/Core/Banshee.Services/Makefile.am:
    	* src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs:
    	* src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs: New
    	service and job that save-to-file and move-on-info-save if/when
    	appropriate.
    
    	* src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs: the
    	SchemaPreferences are now created in SaveTrackMetadataServices; use them.
    
    	* src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs: Start
    	the SaveTrackMetadataServices.
    
    	* src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs:
    	Get rid of the old write-to-file and move-on-info-save job queueing; now
    	happens based on the Updated and LastSynced stamps in the db.
    
    	* src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs: Give
    	access to the underlying IDataReader from HyenaDataReader.
    
    	* src/Core/Banshee.Services/Banshee.Collection/MoveOnInfoSaveJob.cs:
    	Remove.
---
 ChangeLog                                          |   30 +++++
 .../Banshee.Base/Tests/TaglibReadWriteTests.cs     |    2 +-
 .../Banshee.Streaming/SaveTrackMetadataJob.cs      |  113 -----------------
 .../Banshee.Core/Banshee.Streaming/StreamTagger.cs |   62 +++++++++
 src/Core/Banshee.Core/Makefile.am                  |    1 -
 .../Banshee.Collection/MoveOnInfoSaveJob.cs        |   82 ------------
 .../Banshee.Metadata/SaveTrackMetadataJob.cs       |  131 ++++++++++++++++++++
 .../Banshee.Metadata/SaveTrackMetadataService.cs   |  126 +++++++++++++++++++
 .../Banshee.Preferences/PreferenceService.cs       |    9 +-
 .../Banshee.ServiceStack/ServiceManager.cs         |    1 +
 src/Core/Banshee.Services/Makefile.am              |    3 +-
 .../Banshee.Gui.TrackEditor/TrackEditorDialog.cs   |   18 ---
 .../Hyena.Data.Sqlite/HyenaSqliteConnection.cs     |    4 +
 13 files changed, 359 insertions(+), 223 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index cebf856..aca54cc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,35 @@
 2009-04-16  Gabriel Burt  <gabriel burt gmail com>
 
+	* src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs:
+	* src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs:
+	* src/Core/Banshee.Core/Banshee.Streaming/SaveTrackMetadataJob.cs:
+	* src/Core/Banshee.Core/Makefile.am: Remove old save job and move its
+	save-to-file logic into StreamTagger.
+
+	* src/Core/Banshee.Services/Makefile.am:
+	* src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs:
+	* src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs: New
+	service and job that save-to-file and move-on-info-save if/when
+	appropriate.
+
+	* src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs: the
+	SchemaPreferences are now created in SaveTrackMetadataServices; use them.
+
+	* src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs: Start
+	the SaveTrackMetadataServices.
+
+	* src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs:
+	Get rid of the old write-to-file and move-on-info-save job queueing; now
+	happens based on the Updated and LastSynced stamps in the db.
+
+	* src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs: Give
+	access to the underlying IDataReader from HyenaDataReader.
+
+	* src/Core/Banshee.Services/Banshee.Collection/MoveOnInfoSaveJob.cs:
+	Remove.
+
+2009-04-16  Gabriel Burt  <gabriel burt gmail com>
+
 	Add a smart, priority-aware, resource-contention-avoiding job scheduler
 	Net result is BPM analysis will pause while importing, etc (BGO #577772).
 	Still need to convert Banshee.Kernel jobs.
diff --git a/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs b/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs
index fac2835..af568d4 100644
--- a/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs
+++ b/src/Core/Banshee.Core/Banshee.Base/Tests/TaglibReadWriteTests.cs
@@ -121,7 +121,7 @@ namespace Banshee.Base.Tests
             track.Year = 1999;
     
             // Save changes
-            new SaveTrackMetadataJob (track).Run ();
+            StreamTagger.SaveToFile (track);
     
             // Read changes
             file = StreamTagger.ProcessUri (uri);
diff --git a/src/Core/Banshee.Core/Banshee.Streaming/SaveTrackMetadataJob.cs b/src/Core/Banshee.Core/Banshee.Streaming/SaveTrackMetadataJob.cs
deleted file mode 100644
index 58dbf70..0000000
--- a/src/Core/Banshee.Core/Banshee.Streaming/SaveTrackMetadataJob.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-// SaveTrackMetadataJob.cs
-//
-// Author:
-//   Aaron Bockover <abockover novell com>
-//
-// Copyright (C) 2006-2007 Novell, Inc.
-//
-// 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 Mono.Unix;
-
-using Banshee.Collection;
-using Banshee.Configuration.Schema;
-
-namespace Banshee.Streaming
-{
-    public class SaveTrackMetadataJob : Banshee.Kernel.IInstanceCriticalJob
-    {
-        private TrackInfo track;
-        
-        public string Name {
-            get { return String.Format (Catalog.GetString ("Saving tags for {0}"), track.TrackTitle); }
-        }
-        
-        public SaveTrackMetadataJob (TrackInfo track)
-        {
-            this.track = track;
-        }
-    
-        public void Run ()
-        {
-            if (!LibrarySchema.WriteMetadata.Get ()) {
-                Console.WriteLine ("Skipping scheduled metadata write, preference disabled after scheduling");
-                return;
-            }
-            
-            // FIXME taglib# does not seem to handle writing metadata to video files well at all atm
-            // so not allowing
-            if ((track.MediaAttributes & TrackMediaAttributes.VideoStream) != 0) {
-                Hyena.Log.DebugFormat ("Avoiding 100% cpu bug with taglib# by not writing metadata to video file {0}", track);
-                return;
-            }
-        
-            // Note: this should be kept in sync with the metadata read in StreamTagger.cs
-            TagLib.File file = StreamTagger.ProcessUri (track.Uri);
-            if (file == null) {
-                return;
-            }
-            
-            file.Tag.Performers = new string [] { track.ArtistName };
-            file.Tag.PerformersSort = new string [] { track.ArtistNameSort };
-            file.Tag.Album = track.AlbumTitle;
-            file.Tag.AlbumSort = track.AlbumTitleSort;
-            file.Tag.AlbumArtists = track.AlbumArtist == null ? new string [0] : new string [] {track.AlbumArtist};
-            file.Tag.AlbumArtistsSort = (track.AlbumArtistSort == null ? new string [0] : new string [] {track.AlbumArtistSort});
-            // Bug in taglib-sharp-2.0.3.0: Crash if you send it a genre of "{ null }"
-            // on a song with both ID3v1 and ID3v2 metadata. It's happy with "{}", though.
-            // (see http://forum.taglib-sharp.com/viewtopic.php?f=5&t=239 )
-            file.Tag.Genres = (track.Genre == null) ? new string[] {} : new string [] { track.Genre };
-            file.Tag.Title = track.TrackTitle;
-            file.Tag.TitleSort = track.TrackTitleSort;
-            file.Tag.Track = (uint)track.TrackNumber;
-            file.Tag.TrackCount = (uint)track.TrackCount;
-            file.Tag.Composers = new string [] { track.Composer };
-            file.Tag.Conductor = track.Conductor;
-            file.Tag.Grouping = track.Grouping;
-            file.Tag.Copyright = track.Copyright;
-            file.Tag.Comment = track.Comment;
-            file.Tag.Disc = (uint)track.DiscNumber;
-            file.Tag.DiscCount = (uint)track.DiscCount;
-            file.Tag.Year = (uint)track.Year;
-            file.Tag.BeatsPerMinute = (uint)track.Bpm;
-            
-            SaveIsCompilation (file.Tag, track.IsCompilation);
-            file.Save ();
-        }
-        
-        private static void SaveIsCompilation (TagLib.Tag tag, bool is_compilation)
-        {
-            TagLib.Id3v2.Tag id3v2_tag = tag as TagLib.Id3v2.Tag;
-            if (id3v2_tag != null) {
-                id3v2_tag.IsCompilation = is_compilation;
-                return;
-            }
-
-            TagLib.Mpeg4.AppleTag apple_tag = tag as TagLib.Mpeg4.AppleTag;
-            if (apple_tag != null) {
-                apple_tag.IsCompilation = is_compilation;
-                return;
-            }
-        }
-    }
-}
diff --git a/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs b/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
index f8fc1a5..153f02d 100644
--- a/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
+++ b/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
@@ -206,6 +206,68 @@ namespace Banshee.Streaming
             }
             return false;
         }
+
+        private static void SaveIsCompilation (TagLib.Tag tag, bool is_compilation)
+        {
+            TagLib.Id3v2.Tag id3v2_tag = tag as TagLib.Id3v2.Tag;
+            if (id3v2_tag != null) {
+                id3v2_tag.IsCompilation = is_compilation;
+                return;
+            }
+
+            TagLib.Mpeg4.AppleTag apple_tag = tag as TagLib.Mpeg4.AppleTag;
+            if (apple_tag != null) {
+                apple_tag.IsCompilation = is_compilation;
+                return;
+            }
+        }
+
+        public static void SaveToFile (TrackInfo track)
+        {
+            // FIXME taglib# does not seem to handle writing metadata to video files well at all atm
+            // so not allowing
+            if ((track.MediaAttributes & TrackMediaAttributes.VideoStream) != 0) {
+                Hyena.Log.DebugFormat ("Avoiding 100% cpu bug with taglib# by not writing metadata to video file {0}", track);
+                return;
+            }
+        
+            // Note: this should be kept in sync with the metadata read in StreamTagger.cs
+            TagLib.File file = ProcessUri (track.Uri);
+            if (file == null) {
+                return;
+            }
+            
+            file.Tag.Performers = new string [] { track.ArtistName };
+            file.Tag.PerformersSort = new string [] { track.ArtistNameSort };
+            file.Tag.Album = track.AlbumTitle;
+            file.Tag.AlbumSort = track.AlbumTitleSort;
+            file.Tag.AlbumArtists = track.AlbumArtist == null ? new string [0] : new string [] {track.AlbumArtist};
+            file.Tag.AlbumArtistsSort = (track.AlbumArtistSort == null ? new string [0] : new string [] {track.AlbumArtistSort});
+            // Bug in taglib-sharp-2.0.3.0: Crash if you send it a genre of "{ null }"
+            // on a song with both ID3v1 and ID3v2 metadata. It's happy with "{}", though.
+            // (see http://forum.taglib-sharp.com/viewtopic.php?f=5&t=239 )
+            file.Tag.Genres = (track.Genre == null) ? new string[] {} : new string [] { track.Genre };
+            file.Tag.Title = track.TrackTitle;
+            file.Tag.TitleSort = track.TrackTitleSort;
+            file.Tag.Track = (uint)track.TrackNumber;
+            file.Tag.TrackCount = (uint)track.TrackCount;
+            file.Tag.Composers = new string [] { track.Composer };
+            file.Tag.Conductor = track.Conductor;
+            file.Tag.Grouping = track.Grouping;
+            file.Tag.Copyright = track.Copyright;
+            file.Tag.Comment = track.Comment;
+            file.Tag.Disc = (uint)track.DiscNumber;
+            file.Tag.DiscCount = (uint)track.DiscCount;
+            file.Tag.Year = (uint)track.Year;
+            file.Tag.BeatsPerMinute = (uint)track.Bpm;
+            
+            SaveIsCompilation (file.Tag, track.IsCompilation);
+            file.Save ();
+
+            track.FileSize = Banshee.IO.File.GetSize (track.Uri);
+            track.FileModifiedStamp = Banshee.IO.File.GetModifiedTime (track.Uri);
+            track.LastSyncedStamp = DateTime.Now;
+        }
     
         public static void TrackInfoMerge (TrackInfo track, StreamTag tag)
         {
diff --git a/src/Core/Banshee.Core/Makefile.am b/src/Core/Banshee.Core/Makefile.am
index 70d8753..219235b 100644
--- a/src/Core/Banshee.Core/Makefile.am
+++ b/src/Core/Banshee.Core/Makefile.am
@@ -61,7 +61,6 @@ SOURCES =  \
 	Banshee.Kernel/JobPriority.cs \
 	Banshee.Kernel/Scheduler.cs \
 	Banshee.Streaming/CommonTags.cs \
-	Banshee.Streaming/SaveTrackMetadataJob.cs \
 	Banshee.Streaming/StreamPlaybackError.cs \
 	Banshee.Streaming/StreamTag.cs \
 	Banshee.Streaming/StreamTagger.cs
diff --git a/src/Core/Banshee.Services/Banshee.Collection/MoveOnInfoSaveJob.cs b/src/Core/Banshee.Services/Banshee.Collection/MoveOnInfoSaveJob.cs
deleted file mode 100644
index a81b12a..0000000
--- a/src/Core/Banshee.Services/Banshee.Collection/MoveOnInfoSaveJob.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// MoveOnInfoSaveJob.cs
-//
-// Author:
-//   Ruben Vermeersch <ruben savanne be>
-//
-// Copyright (C) 2006-2008 Novell, Inc.
-//
-// 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 Banshee.Base;
-using Banshee.Collection.Database;
-using Banshee.Configuration.Schema;
-
-namespace Banshee.Collection
-{
-    public class MoveOnInfoSaveJob : Banshee.Kernel.IInstanceCriticalJob
-    {
-        private DatabaseTrackInfo track;
-
-        public string Name {
-            get { return String.Format (Catalog.GetString ("Renaming {0}"), track.TrackTitle); }
-        }
-
-        public MoveOnInfoSaveJob (DatabaseTrackInfo track)
-        {
-            this.track = track;
-        }
-
-        public void Run ()
-        {
-            if (track == null) {
-                return;
-            }
-
-            if (!LibrarySchema.MoveOnInfoSave.Get ()) {
-                Hyena.Log.Debug ("Skipping scheduled rename, preference disabled after scheduling");
-                return;
-            }
-
-            SafeUri old_uri = track.Uri;
-            bool in_library = old_uri.AbsolutePath.StartsWith (track.PrimarySource.BaseDirectoryWithSeparator);
-
-            if (!in_library) {
-                return;
-            }
-
-            string new_filename = FileNamePattern.BuildFull (track.PrimarySource.BaseDirectory, track, Path.GetExtension (old_uri.ToString ()));
-            SafeUri new_uri = new SafeUri (new_filename);
-
-            if (!new_uri.Equals (old_uri) && !Banshee.IO.File.Exists (new_uri)) {
-                Banshee.IO.File.Move (old_uri, new_uri);
-                Banshee.IO.Utilities.TrimEmptyDirectories (old_uri);
-                track.Uri = new_uri;
-                track.Save ();
-            }
-        }
-    }
-}
diff --git a/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs
new file mode 100644
index 0000000..bdcdac3
--- /dev/null
+++ b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataJob.cs
@@ -0,0 +1,131 @@
+//
+// SaveTrackMetadataJob.cs
+//
+// Authors:
+//   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
+//   Ruben Vermeersch <ruben savanne be>
+//
+// Copyright (C) 2006-2009 Novell, Inc.
+//
+// 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 Mono.Unix;
+
+using Hyena.Jobs;
+using Hyena.Data.Sqlite;
+
+using Banshee.Base;
+using Banshee.Streaming;
+using Banshee.Collection.Database;
+using Banshee.Library;
+using Banshee.ServiceStack;
+using Banshee.Configuration.Schema;
+using Banshee.Preferences;
+
+namespace Banshee.Metadata
+{
+    public class SaveTrackMetadataJob : DbIteratorJob
+    {
+        private LibrarySource source = ServiceManager.SourceManager.MusicLibrary;
+
+        public SaveTrackMetadataJob () : base (Catalog.GetString ("Saving Metadata to File"))
+        {
+            SetResources (Resource.Cpu, Resource.Disk, Resource.Database);
+            IsBackground = true;
+
+            CountCommand = new HyenaSqliteCommand (
+                "SELECT COUNT(*) FROM CoreTracks WHERE DateUpdatedStamp > LastSyncedStamp AND PrimarySourceID = ?",
+                source.DbId
+            );
+
+            SelectCommand = DatabaseTrackInfo.Provider.CreateFetchCommand (String.Format (
+                "DateUpdatedStamp > LastSyncedStamp AND PrimarySourceID = {0}", source.DbId)
+            );
+        }
+
+        public bool WriteEnabled { get; set; }
+        public bool RenameEnabled { get; set; }
+
+        private HyenaSqliteCommand update_synced_at;
+    
+        protected override void IterateCore (HyenaDataReader reader)
+        {
+            DatabaseTrackInfo track = DatabaseTrackInfo.Provider.Load (reader.Reader);
+
+            bool wrote = false;
+            bool renamed = false;
+            try {
+                if (WriteEnabled) {
+                    Hyena.Log.DebugFormat ("Saving metadata for {0}", track);
+                    StreamTagger.SaveToFile (track);
+                    wrote = true;
+                }
+
+                if (RenameEnabled) {
+                    Hyena.Log.DebugFormat ("Updating file name for {0}", track);
+                    renamed = RenameFile (track);
+                    if (renamed && !wrote) {
+                        track.LastSyncedStamp = DateTime.Now;
+                    }
+                }
+            } catch (Exception) {
+                Hyena.Log.ErrorFormat ("Error writing to or renaming {0}", track);
+            } finally {
+                if (wrote || renamed) {
+                    // Save the resulting changes to FileSize, LastSyncedStamp, possibly to Uri, etc
+                    // TODO need to clear track model caches if URI changed
+                    track.Save (false);
+                } else {
+                    if (update_synced_at == null) {
+                        update_synced_at = new HyenaSqliteCommand (
+                            "UPDATE CoreTracks SET LastSyncedStamp = ? WHERE TrackID = ?");
+                    }
+
+                    ServiceManager.DbConnection.Execute (update_synced_at, DateTime.Now, track.TrackId);
+                }
+            }
+        }
+
+        private bool RenameFile (DatabaseTrackInfo track)
+        {
+            SafeUri old_uri = track.Uri;
+            bool in_library = old_uri.AbsolutePath.StartsWith (source.BaseDirectoryWithSeparator);
+
+            if (!in_library) {
+                return false;
+            }
+
+            string new_filename = FileNamePattern.BuildFull (source.BaseDirectory, track, System.IO.Path.GetExtension (old_uri.ToString ()));
+            SafeUri new_uri = new SafeUri (new_filename);
+
+            if (!new_uri.Equals (old_uri) && !Banshee.IO.File.Exists (new_uri)) {
+                Banshee.IO.File.Move (old_uri, new_uri);
+                Banshee.IO.Utilities.TrimEmptyDirectories (old_uri);
+                track.Uri = new_uri;
+                return true;
+            }
+
+            return false;
+        }
+    }
+}
diff --git a/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs
new file mode 100644
index 0000000..aa4da0d
--- /dev/null
+++ b/src/Core/Banshee.Services/Banshee.Metadata/SaveTrackMetadataService.cs
@@ -0,0 +1,126 @@
+//
+// SaveTrackMetadataService.cs
+//
+// Author:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 2009 Novell, Inc.
+//
+// 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 Mono.Unix;
+
+using Hyena.Jobs;
+using Hyena.Data.Sqlite;
+
+using Banshee.Streaming;
+using Banshee.Collection.Database;
+using Banshee.Sources;
+using Banshee.ServiceStack;
+using Banshee.Configuration.Schema;
+using Banshee.Preferences;
+
+namespace Banshee.Metadata
+{
+    public class SaveTrackMetadataService : IService
+    {
+        public static SchemaPreference<bool> WriteEnabled = new SchemaPreference<bool> (
+                LibrarySchema.WriteMetadata, 
+                Catalog.GetString ("Write _metadata to files"),
+                Catalog.GetString ("Enable this option to save tags and other metadata inside supported audio files.")
+        );
+
+        public static SchemaPreference<bool> RenameEnabled = new SchemaPreference<bool> (
+                LibrarySchema.MoveOnInfoSave,
+                Catalog.GetString ("_Update file and folder names"),
+                Catalog.GetString ("Enabling this option ensures that files and folders are renamed according to the metadata.")
+        );
+
+        private SaveTrackMetadataJob job;
+        private object sync = new object ();
+        private bool inited = false;
+
+        public SaveTrackMetadataService ()
+        {
+            Banshee.ServiceStack.Application.RunTimeout (10000, delegate {
+                WriteEnabled.ValueChanged += OnEnabledChanged;
+                RenameEnabled.ValueChanged += OnEnabledChanged;
+                ServiceManager.SourceManager.MusicLibrary.TracksChanged += OnTracksChanged;
+                Save ();
+                inited = true;
+                return false;
+            });
+        }
+
+        public void Dispose ()
+        {
+            if (inited) {
+                ServiceManager.SourceManager.MusicLibrary.TracksChanged -= OnTracksChanged;
+
+                if (job != null) {
+                    ServiceManager.JobScheduler.Cancel (job);
+                }
+            }
+        }
+
+        private void Save ()
+        {
+            if (!(WriteEnabled.Value || RenameEnabled.Value))
+                return;
+
+            lock (sync) {
+                if (job != null) {
+                    job.WriteEnabled  = WriteEnabled.Value;
+                    job.RenameEnabled = RenameEnabled.Value;
+                    return;
+                } else {
+                    job = new SaveTrackMetadataJob ();
+                    job.WriteEnabled  = WriteEnabled.Value;
+                    job.RenameEnabled = RenameEnabled.Value;
+                }
+            }
+
+            job.Finished += delegate { job = null; };
+            job.Register ();
+        }
+
+        private void OnTracksChanged (Source sender, TrackEventArgs args)
+        {
+            Save ();
+        }
+
+        private void OnEnabledChanged (Root pref)
+        {
+            if (WriteEnabled.Value || RenameEnabled.Value) {
+                Save ();
+            } else {
+                if (job != null) {
+                    ServiceManager.JobScheduler.Cancel (job);
+                }
+            }
+        }
+
+        string IService.ServiceName {
+            get { return "SaveMetadataService"; }
+        }
+    }
+}
diff --git a/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs b/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs
index 07431f5..3a6b326 100644
--- a/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs
+++ b/src/Core/Banshee.Services/Banshee.Preferences/PreferenceService.cs
@@ -56,14 +56,9 @@ namespace Banshee.Preferences
             policies.Add (new SchemaPreference<bool> (LibrarySchema.CopyOnImport, 
                 Catalog.GetString ("Co_py files to media folders when importing")));
             
-            policies.Add (new SchemaPreference<bool> (LibrarySchema.WriteMetadata, 
-                Catalog.GetString ("Write _metadata to files"),
-                Catalog.GetString ("Enable this option to save tags and other metadata inside supported audio files.")));
+            policies.Add (Banshee.Metadata.SaveTrackMetadataService.WriteEnabled);
+            policies.Add (Banshee.Metadata.SaveTrackMetadataService.RenameEnabled);
 
-            policies.Add (new SchemaPreference<bool> (LibrarySchema.MoveOnInfoSave,
-                Catalog.GetString ("_Update file and folder names"),
-                Catalog.GetString ("Enabling this option ensures that files and folders are renamed according to the metadata.")));
-            
             // Misc section
             general.Add (new Section ("misc", Catalog.GetString ("Miscellaneous"), 20));
         }
diff --git a/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs b/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs
index 0dcdd24..752798b 100644
--- a/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs
+++ b/src/Core/Banshee.Services/Banshee.ServiceStack/ServiceManager.cs
@@ -102,6 +102,7 @@ namespace Banshee.ServiceStack
             RegisterService<JobScheduler> ();
             RegisterService<Banshee.Hardware.HardwareManager> ();
             RegisterService<Banshee.Collection.Indexer.CollectionIndexerService> ();
+            RegisterService<Banshee.Metadata.SaveTrackMetadataService> ();
         }
         
         public static void DefaultInitialize ()
diff --git a/src/Core/Banshee.Services/Makefile.am b/src/Core/Banshee.Services/Makefile.am
index adfbef4..a458bcb 100644
--- a/src/Core/Banshee.Services/Makefile.am
+++ b/src/Core/Banshee.Services/Makefile.am
@@ -41,7 +41,6 @@ SOURCES =  \
 	Banshee.Collection/ImportManager.cs \
 	Banshee.Collection/MemoryTrackListModel.cs \
 	Banshee.Collection/ModelHelper.cs \
-	Banshee.Collection/MoveOnInfoSaveJob.cs \
 	Banshee.Collection/RescanPipeline.cs \
 	Banshee.Collection/SelectAllSelection.cs \
 	Banshee.Collection/TrackListModel.cs \
@@ -110,6 +109,8 @@ SOURCES =  \
 	Banshee.Metadata/IMetadataProvider.cs \
 	Banshee.Metadata/MetadataService.cs \
 	Banshee.Metadata/MetadataServiceJob.cs \
+	Banshee.Metadata/SaveTrackMetadataJob.cs \
+	Banshee.Metadata/SaveTrackMetadataService.cs \
 	Banshee.Networking/Network.cs \
 	Banshee.Networking/NetworkManager.cs \
 	Banshee.PlaybackController/IBasicPlaybackController.cs \
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs b/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
index d0cba97..099bb66 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui.TrackEditor/TrackEditorDialog.cs
@@ -543,29 +543,11 @@ namespace Banshee.Gui.TrackEditor
             TrackInfo.ExportableMerge (track, track.SourceTrack);
             track.SourceTrack.Save ();
                 
-            if (LibrarySchema.WriteMetadata.Get ()) {
-                SaveToFile (track.SourceTrack);
-            }
-
-            if (LibrarySchema.MoveOnInfoSave.Get ()) {
-                MoveSavedFile (track.SourceTrack);
-            }
-
             if (track.SourceTrack == ServiceManager.PlayerEngine.CurrentTrack) {
                 ServiceManager.PlayerEngine.TrackInfoUpdated ();
             }
         }
         
-        private void SaveToFile (TrackInfo track)
-        {
-            Scheduler.Schedule (new Banshee.Streaming.SaveTrackMetadataJob (track), JobPriority.Highest);
-        }
-
-        private void MoveSavedFile (TrackInfo track)
-        {
-            Scheduler.Schedule (new MoveOnInfoSaveJob (track as DatabaseTrackInfo), JobPriority.Highest);
-        }
-
 #endregion
 
 #region Static Helpers
diff --git a/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs b/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
index 5019598..e3c059d 100644
--- a/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
+++ b/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
@@ -39,6 +39,10 @@ namespace Hyena.Data.Sqlite
     {
         private IDataReader reader;
         private bool read = false;
+
+        public IDataReader Reader {
+            get { return reader; }
+        }
         
         public HyenaDataReader (IDataReader reader)
         {



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