banshee r4521 - in trunk/banshee: . src/Backends/Banshee.Unix/Banshee.IO.Unix src/Core/Banshee.Core/Banshee.Collection src/Core/Banshee.Core/Banshee.IO src/Core/Banshee.Core/Banshee.IO.SystemIO src/Core/Banshee.Core/Banshee.Streaming src/Core/Banshee.Services src/Core/Banshee.Services/Banshee.Collection src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Database src/Core/Banshee.ThickClient/Banshee.Gui src/Core/Banshee.ThickClient/Resources src/Libraries/Hyena/Hyena.Collections src/Libraries/Hyena/Hyena.Data.Sqlite



Author: gburt
Date: Fri Sep 12 01:35:43 2008
New Revision: 4521
URL: http://svn.gnome.org/viewvc/banshee?rev=4521&view=rev

Log:
2008-09-11  Gabriel Burt  <gabriel burt gmail com>

	* src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs:
	* src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml: Add
	Rescan action that triggers rescan of the Music Library folder.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs:
	Remove unnecessary DateAdded set.

	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs:
	* src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs: Add
	FileModifiedStamp and LastSyncedStamp properties.

	* src/Core/Banshee.Services/Makefile.am:
	* src/Core/Banshee.Services/Banshee.Services.csproj:
	* src/Core/Banshee.Services/Banshee.Collection/RescanPipeline.cs: New file
	that scans all files in a Library's BaseDirectory, importing new items,
	removing items no longer on disk, and identifying items that were renamed.

	* src/Libraries/Hyena/Hyena.Collections/QueuePipelineElement.cs:
	* src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs: Factor a
	lot of the counting of the ImportElement up into QueuePipelineElement.

	* src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs:
	Add the LastSynced and FileModified columns.

	* src/Core/Banshee.Core/Banshee.IO.SystemIO/File.cs:
	* src/Core/Banshee.Core/Banshee.IO/File.cs:
	* src/Core/Banshee.Core/Banshee.IO/IFile.cs:
	* src/Backends/Banshee.Unix/Banshee.IO.Unix/File.cs: Add GetModifiedTime
	method.

	* src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs: Set
	FileModified and LastSynced stamps.

	* src/Libraries/Hyena/Hyena.Collections/QueuePipeline.cs: Make sure no
	elements still have items left to process when considering if the pipeline
	is finished/empty.

	* src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs: Add
	CreateFetchCommand method that returns a HyenaSqliteCommand useful for
	fetching items from a model with a custom condition/limit/order.



Added:
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/RescanPipeline.cs
Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/src/Backends/Banshee.Unix/Banshee.IO.Unix/File.cs
   trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
   trunk/banshee/src/Core/Banshee.Core/Banshee.IO.SystemIO/File.cs
   trunk/banshee/src/Core/Banshee.Core/Banshee.IO/File.cs
   trunk/banshee/src/Core/Banshee.Core/Banshee.IO/IFile.cs
   trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Services.csproj
   trunk/banshee/src/Core/Banshee.Services/Makefile.am
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
   trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipeline.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipelineElement.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs

Modified: trunk/banshee/src/Backends/Banshee.Unix/Banshee.IO.Unix/File.cs
==============================================================================
--- trunk/banshee/src/Backends/Banshee.Unix/Banshee.IO.Unix/File.cs	(original)
+++ trunk/banshee/src/Backends/Banshee.Unix/Banshee.IO.Unix/File.cs	Fri Sep 12 01:35:43 2008
@@ -40,6 +40,7 @@
         private Stat buf;
         private bool is_directory;
         private bool is_regular_file;
+        private long mtime;
         
         internal FileStat (string path)
         {
@@ -49,6 +50,7 @@
             is_directory &= (buf.st_mode & FilePermissions.S_IFDIR) == FilePermissions.S_IFDIR;
             // FIXME: workaround for http://bugzilla.ximian.com/show_bug.cgi?id=76966
             is_directory &= ! ((buf.st_mode & FilePermissions.S_IFSOCK) == FilePermissions.S_IFSOCK);
+            mtime = buf.st_mtime;
         }
         
         internal bool IsDirectory {
@@ -58,6 +60,10 @@
         internal bool IsRegularFile {
             get { return is_regular_file; }
         }
+
+        internal long MTime {
+            get { return mtime; }
+        }
     }
 
     public class File : IFile
@@ -73,7 +79,7 @@
             FileStat stat = new FileStat (uri.LocalPath);
             return stat.IsRegularFile && !stat.IsDirectory;
         }
-        
+
         public void Move (SafeUri from, SafeUri to)
         {
             Mono.Unix.Native.Stdlib.rename (from.LocalPath, to.LocalPath);
@@ -106,5 +112,11 @@
                 return -1;
             }
         }
+
+        public long GetModifiedTime (SafeUri uri)
+        {
+            FileStat stat = new FileStat (uri.LocalPath);
+            return stat.MTime;
+        }
     }
 }

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.Collection/TrackInfo.cs	Fri Sep 12 01:35:43 2008
@@ -62,6 +62,7 @@
         private SafeUri more_info_uri;
         private string mimetype;
         private long filesize;
+        private long file_mtime;
 
         private string artist_name;
         private string album_title;
@@ -89,6 +90,7 @@
         private TimeSpan duration;
         private DateTime release_date;
         private DateTime date_added;
+        private DateTime last_synced;
 
         private int play_count;
         private int skip_count;
@@ -174,6 +176,16 @@
             set { filesize = value; }
         }
 
+        public virtual long FileModifiedStamp {
+            get { return file_mtime; }
+            set { file_mtime = value; }
+        }
+
+        public virtual DateTime LastSyncedStamp {
+            get { return last_synced; }
+            set { last_synced = value; }
+        }
+
         [Exportable (ExportName = "artist")]
         public virtual string ArtistName {
             get { return artist_name; }

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.IO.SystemIO/File.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.IO.SystemIO/File.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.IO.SystemIO/File.cs	Fri Sep 12 01:35:43 2008
@@ -63,6 +63,11 @@
                 return -1;
             }
         }
+
+        public long GetModifiedTime (SafeUri uri)
+        {
+            return Hyena.DateTimeUtil.FromDateTime (new System.IO.FileInfo (uri.LocalPath).LastWriteTime);
+        }
         
         public System.IO.Stream OpenRead (SafeUri uri)
         {

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.IO/File.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.IO/File.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.IO/File.cs	Fri Sep 12 01:35:43 2008
@@ -43,7 +43,7 @@
     	{
     	    return Provider.File.Exists (uri);
     	}
-
+        
     	public static void Move (SafeUri from, SafeUri to)
     	{
     	    Provider.File.Move (from, to);
@@ -59,6 +59,11 @@
     	    return Provider.File.GetSize (uri);
     	}
 
+        public static long GetModifiedTime (SafeUri uri)
+        {
+            return Provider.File.GetModifiedTime (uri);
+        }
+
     	public static System.IO.Stream OpenRead (SafeUri uri)
     	{
     	    return Provider.File.OpenRead (uri);

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.IO/IFile.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.IO/IFile.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.IO/IFile.cs	Fri Sep 12 01:35:43 2008
@@ -39,6 +39,7 @@
         void Move (SafeUri from, SafeUri to);
         void Copy (SafeUri from, SafeUri to, bool overwrite);
         long GetSize (SafeUri uri);
+        long GetModifiedTime (SafeUri uri);
         
         System.IO.Stream OpenRead (SafeUri uri);
         System.IO.Stream OpenWrite (SafeUri uri, bool overwrite);

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.Streaming/StreamTagger.cs	Fri Sep 12 01:35:43 2008
@@ -112,6 +112,8 @@
             track.Uri = new SafeUri (file.Name);
             track.MimeType = file.MimeType;
             track.FileSize = Banshee.IO.File.GetSize (track.Uri);
+            track.FileModifiedStamp = Banshee.IO.File.GetModifiedTime (track.Uri);
+            track.LastSyncedStamp = DateTime.Now;
             track.BitRate  = file.Properties.AudioBitrate;
             track.Duration = file.Properties.Duration;
             

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseImportManager.cs	Fri Sep 12 01:35:43 2008
@@ -189,8 +189,6 @@
                 TagLib.File file = StreamTagger.ProcessUri (uri);
                 track = new DatabaseTrackInfo ();
                 StreamTagger.TrackInfoMerge (track, file);
-                
-                track.DateAdded = DateTime.Now;
                 track.PrimarySource = trackPrimarySourceChooser (track);
 
                 if (track.PrimarySource is Banshee.Library.LibrarySource) {

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs	Fri Sep 12 01:35:43 2008
@@ -346,6 +346,18 @@
             get { return base.FileSize; }
             set { base.FileSize = value; }
         }
+
+        [DatabaseColumn]
+        public override long FileModifiedStamp {
+            get { return base.FileModifiedStamp; }
+            set { base.FileModifiedStamp = value; }
+        }
+
+        [DatabaseColumn]
+        public override DateTime LastSyncedStamp {
+            get { return base.LastSyncedStamp; }
+            set { base.LastSyncedStamp = value; }
+        }
         
         [DatabaseColumn ("Attributes")]
         public override TrackMediaAttributes MediaAttributes {

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/ImportManager.cs	Fri Sep 12 01:35:43 2008
@@ -52,40 +52,22 @@
             {
                 this.manager = manager;
             }
-        
-            private int processed_count;
-            public int ProcessedCount {
-                get { return processed_count; }
-            }
-            
-            private int total_count;
-            public int TotalCount {
-                get { return total_count; }
-            }
-            
+
             public override void Enqueue (string item)
             {
-                total_count++;
-                manager.UpdateScannerProgress ();
                 base.Enqueue (item);
+                manager.UpdateScannerProgress ();
             }
         
             protected override string ProcessItem (string item)
             {
                 try {
                     manager.OnImportRequested (item);
-                    processed_count++;
                 } catch (Exception e) {
                     Hyena.Log.Exception (e);
                 }
                 return null;   
             }
-            
-            public void Reset ()
-            {
-                processed_count = 0;
-                total_count = 0;
-            }
         }
         
 #endregion
@@ -160,8 +142,6 @@
                 if (!KeepUserJobHidden) {
                     user_job.Register ();
                 }
-                
-                import_element.Reset ();
             }
         }
         
@@ -179,8 +159,6 @@
                 user_job.CancelRequested -= OnCancelRequested;
                 user_job.Finish ();
                 user_job = null;
-                    
-                import_element.Reset ();
             }
         }
         

Added: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/RescanPipeline.cs
==============================================================================
--- (empty file)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection/RescanPipeline.cs	Fri Sep 12 01:35:43 2008
@@ -0,0 +1,208 @@
+//
+// RescanPipeline.cs
+//
+// Authors:
+//   Gabriel Burt <gburt novell com>
+//
+// Copyright (C) 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.Data;
+
+using Mono.Unix;
+
+using Hyena.Collections;
+using Hyena.Data.Sqlite;
+
+using Banshee.Base;
+using Banshee.Sources;
+using Banshee.Collection.Database;
+using Banshee.Library;
+using Banshee.ServiceStack;
+
+namespace Banshee.Collection
+{
+    // Goals:
+    // 1. Add new files that are on disk but not in the library
+    // 2. Find the updated location of files that were moved
+    // 3. Update metadata for files that were changed since we last scanned/imported
+    // 4. Remove tracks that aren't on disk and weren't found to have moved
+    //
+    // Approach:
+    // 1. For each file in the source's directory, find oraphend db track if any, or add if new 
+    //    and update if modified; update the LastScannedAt stamp
+    // 2. Remove all db tracks from the database that weren't scanned (LastScannedAt < scan_started)
+    public class RescanPipeline : QueuePipeline<string>
+    {
+        private DateTime scan_started;
+        private PrimarySource psource;
+        private BatchUserJob job;
+        private TrackSyncPipelineElement track_sync;
+
+        public RescanPipeline (LibrarySource psource) : base ()
+        {
+            this.psource = psource;
+            scan_started = DateTime.Now;
+
+            AddElement (new Banshee.IO.DirectoryScannerPipelineElement ());
+            AddElement (track_sync = new TrackSyncPipelineElement (psource, scan_started));
+            Finished += OnFinished;
+            
+            BuildJob ();
+            Enqueue (psource.BaseDirectory);
+        }
+
+        private void BuildJob ()
+        {
+            job = new BatchUserJob (Catalog.GetString ("Rescanning {0} of {1}"), "system-search", "gtk-find");
+            job.CanCancel = true;
+            job.CancelRequested += delegate { cancelled = true; Cancel (); };
+            track_sync.ProcessedItem += delegate {
+                job.Total = track_sync.TotalCount;
+                job.Completed = track_sync.ProcessedCount;
+                job.Status = track_sync.Status;
+            };
+            job.Register ();
+        }
+
+        private bool cancelled = false;
+        private void OnFinished (object o, EventArgs args)
+        {
+            job.Finish ();
+
+            if (cancelled) {
+                return;
+            }
+
+            //Hyena.Log.DebugFormat ("Have {0} items before delete", ServiceManager.DbConnection.Query<int>("select count(*) from coretracks where primarysourceid=?", psource.DbId));
+
+            // Delete tracks that are under the BaseDirectory (UriType = relative) and that weren't rescanned just now
+            ServiceManager.DbConnection.Execute (@"BEGIN;
+                DELETE FROM CorePlaylistEntries WHERE TrackID IN 
+                    (SELECT TrackID FROM CoreTracks WHERE PrimarySourceID = ? AND UriType = ? AND LastSyncedStamp IS NOT NULL AND LastSyncedStamp < ?);
+                DELETE FROM CoreSmartPlaylistEntries WHERE TrackID IN
+                    (SELECT TrackID FROM CoreTracks WHERE PrimarySourceID = ? AND UriType = ? AND LastSyncedStamp IS NOT NULL AND LastSyncedStamp < ?);
+                DELETE FROM CoreTracks WHERE PrimarySourceID = ? AND UriType = ? AND LastSyncedStamp IS NOT NULL AND LastSyncedStamp < ?;
+                COMMIT",
+                psource.DbId, (int)TrackUriType.RelativePath, scan_started,
+                psource.DbId, (int)TrackUriType.RelativePath, scan_started,
+                psource.DbId, (int)TrackUriType.RelativePath, scan_started
+            );
+
+            // TODO prune artists/albums
+            psource.Reload ();
+            psource.NotifyTracksChanged ();
+            //Hyena.Log.DebugFormat ("Have {0} items after delete", ServiceManager.DbConnection.Query<int>("select count(*) from coretracks where primarysourceid=?", psource.DbId));
+        }
+    }
+
+    public class TrackSyncPipelineElement : QueuePipelineElement<string>
+    {
+        private PrimarySource psource;
+        private DateTime scan_started;
+        private HyenaSqliteCommand fetch_command, fetch_similar_command;
+
+        private string status;
+        public string Status {
+            get { return status; }
+        }
+
+        public TrackSyncPipelineElement (PrimarySource psource, DateTime scan_started) : base ()
+        {
+            this.psource = psource;
+            this.scan_started = scan_started;
+
+            fetch_command = DatabaseTrackInfo.Provider.CreateFetchCommand (
+                "CoreTracks.PrimarySourceID = ? AND (CoreTracks.Uri = ? OR CoreTracks.Uri = ?) LIMIT 1");
+
+            fetch_similar_command = DatabaseTrackInfo.Provider.CreateFetchCommand (
+                "CoreTracks.PrimarySourceID = ? AND CoreTracks.LastSyncedStamp < ? AND CoreTracks.MetadataHash = ?");
+        }
+
+        protected override string ProcessItem (string file_path)
+        {
+            if (!DatabaseImportManager.IsWhiteListedFile (file_path)) {
+                return null;
+            }
+
+            //Hyena.Log.DebugFormat ("Rescanning item {0}", file_path);
+            try {
+                string relative_path = Banshee.Base.Paths.MakePathRelative (file_path, psource.BaseDirectory);
+                
+                IDataReader reader = ServiceManager.DbConnection.Query (fetch_command, psource.DbId, file_path, relative_path);
+                if (reader.Read () ) {
+                    //Hyena.Log.DebugFormat ("Found it in the db!");
+                    DatabaseTrackInfo track = DatabaseTrackInfo.Provider.Load (reader);
+                    
+                    MergeIfModified (track);
+    
+                    // Either way, update the LastSyncStamp
+                    track.LastSyncedStamp = DateTime.Now;
+                    track.Save (false);
+                    status = String.Format ("{0} - {1}", track.DisplayArtistName, track.DisplayTrackTitle);
+                } else {
+                    // This URI is not in the database - try to find it based on MetadataHash in case it was simply moved
+                    DatabaseTrackInfo track = new DatabaseTrackInfo ();
+                    TagLib.File file = Banshee.Streaming.StreamTagger.ProcessUri (new SafeUri (file_path));
+                    Banshee.Streaming.StreamTagger.TrackInfoMerge (track, file);
+    
+                    IDataReader similar_reader = ServiceManager.DbConnection.Query (fetch_similar_command, psource.DbId, scan_started, track.MetadataHash);
+                    DatabaseTrackInfo similar_track = null;
+                    while (similar_reader.Read ()) {
+                        similar_track = DatabaseTrackInfo.Provider.Load (similar_reader);
+                        if (!Banshee.IO.File.Exists (similar_track.Uri)) {
+                            //Hyena.Log.DebugFormat ("Apparently {0} was moved to {1}", similar_track.Uri, file_path);
+                            similar_track.Uri = new SafeUri (file_path);
+                            MergeIfModified (similar_track);
+                            similar_track.LastSyncedStamp = DateTime.Now;
+                            similar_track.Save (false);
+                            status = String.Format ("{0} - {1}", similar_track.DisplayArtistName, similar_track.DisplayTrackTitle);
+                            break;
+                        }
+                        similar_track = null;
+                    }
+    
+                    // If we still couldn't find it, try to import it
+                    if (similar_track == null) {
+                        //Hyena.Log.DebugFormat ("Couldn't find it, so queueing to import it");
+                        ServiceManager.Get<Banshee.Library.LibraryImportManager> ().Enqueue (file_path);
+                    }
+                }
+            } catch (Exception e) {
+                Hyena.Log.Exception (e);
+            }
+            return null;
+        }
+
+        private void MergeIfModified (TrackInfo track)
+        {
+            long mtime = Banshee.IO.File.GetModifiedTime (track.Uri);
+
+            // If the file was modified since we last scanned, parse the file's metadata
+            if (mtime > track.FileModifiedStamp) {
+                TagLib.File file = Banshee.Streaming.StreamTagger.ProcessUri (track.Uri);
+                Banshee.Streaming.StreamTagger.TrackInfoMerge (track, file, false);
+            }
+        }
+    }
+}

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	Fri Sep 12 01:35:43 2008
@@ -52,7 +52,7 @@
         // NOTE: Whenever there is a change in ANY of the database schema,
         //       this version MUST be incremented and a migration method
         //       MUST be supplied to match the new version number
-        protected const int CURRENT_VERSION = 21;
+        protected const int CURRENT_VERSION = 22;
         protected const int CURRENT_METADATA_VERSION = 4;
         
 #region Migration Driver
@@ -537,6 +537,19 @@
         
 #endregion
 
+#region Version 22
+
+        [DatabaseVersion (22)]
+        private bool Migrate_22 ()
+        {
+            Execute ("ALTER TABLE CoreTracks ADD COLUMN LastSyncedStamp INTEGER DEFAULT NULL");
+            Execute ("ALTER TABLE CoreTracks ADD COLUMN FileModifiedStamp INTEGER DEFAULT NULL");
+            Execute ("UPDATE CoreTracks SET LastSyncedStamp = DateAddedStamp;");
+            return true;
+        }
+        
+#endregion
+
 #pragma warning restore 0169
         
 #region Fresh database setup
@@ -623,7 +636,9 @@
                     DateAddedStamp      INTEGER,
                     DateUpdatedStamp    INTEGER,
                     MetadataHash        TEXT,
-                    BPM                 INTEGER
+                    BPM                 INTEGER,
+                    LastSyncedStamp     INTEGER,
+                    FileModifiedStamp   INTEGER
                 )
             ", (int)TrackMediaAttributes.Default, (int)StreamPlaybackError.None));
             Execute("CREATE INDEX CoreTracksPrimarySourceIndex ON CoreTracks(ArtistID, AlbumID, PrimarySourceID, Disc, TrackNumber, Uri)");
@@ -797,7 +812,7 @@
                         NULL,
                         DateAddedStamp,
                         DateAddedStamp,
-                        NULL, NULL
+                        NULL, NULL, DateAddedStamp, NULL
                         FROM Tracks
             ", (int)TrackMediaAttributes.Default, (int)StreamPlaybackError.None));
 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Services.csproj
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Services.csproj	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Services.csproj	Fri Sep 12 01:35:43 2008
@@ -233,6 +233,7 @@
     <Compile Include="Banshee.Collection.Indexer\CollectionIndexer.cs" />
     <Compile Include="Banshee.Configuration\DefaultApplicationHelper.cs" />
     <Compile Include="Banshee.Collection.Indexer\IIndexerClient.cs" />
+    <Compile Include="Banshee.Collection\RescanPipeline.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Banshee.Services.addin.xml" />

Modified: trunk/banshee/src/Core/Banshee.Services/Makefile.am
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Makefile.am	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Makefile.am	Fri Sep 12 01:35:43 2008
@@ -39,6 +39,7 @@
 	Banshee.Collection/MemoryTrackListModel.cs \
 	Banshee.Collection/ModelHelper.cs \
 	Banshee.Collection/MoveOnInfoSaveJob.cs \
+	Banshee.Collection/RescanPipeline.cs \
 	Banshee.Collection/SelectAllSelection.cs \
 	Banshee.Collection/TrackListModel.cs \
 	Banshee.Configuration/DatabaseConfigurationClient.cs \

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.Gui/GlobalActions.cs	Fri Sep 12 01:35:43 2008
@@ -57,6 +57,12 @@
                     Catalog.GetString ("Import Playlist..."), null,
                     Catalog.GetString ("Import a playlist"), OnImportPlaylist),
 
+                new ActionEntry ("RescanAction", null,
+                    Catalog.GetString ("Rescan Music Library"), null,
+                    Catalog.GetString ("Rescan the Music Library folder"), delegate {
+                        new Banshee.Collection.RescanPipeline (ServiceManager.SourceManager.MusicLibrary);
+                    }),
+
                 new ActionEntry ("OpenLocationAction", null, 
                     Catalog.GetString ("Open _Location..."), "<control>L",
                     Catalog.GetString ("Open a remote location for playback"), OnOpenLocation),

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml	Fri Sep 12 01:35:43 2008
@@ -84,6 +84,7 @@
     </menu>
     
     <menu name="ToolsMenu" action="ToolsMenuAction">
+      <menuitem name="Rescan" action="RescanAction"/>
     </menu>
 
     <menu name="HelpMenu" action="HelpMenuAction">

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipeline.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipeline.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipeline.cs	Fri Sep 12 01:35:43 2008
@@ -93,7 +93,7 @@
             lock (sync) {
                 QueuePipelineElement<T> element = FirstElement;
                 while (element != null) {
-                    any_processing |= element.Processing;
+                    any_processing |= element.Processing || element.ProcessedCount < element.TotalCount;
                     if (any_processing) {
                         break;
                     }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipelineElement.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipelineElement.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Collections/QueuePipelineElement.cs	Fri Sep 12 01:35:43 2008
@@ -41,14 +41,25 @@
         #pragma warning disable 0067
         // FIXME: This is to mute gmcs: https://bugzilla.novell.com/show_bug.cgi?id=360455
         public event EventHandler Finished;
+        public event EventHandler ProcessedItem;
         #pragma warning restore 0067
-    
+
         private Queue<T> queue = new Queue<T> ();
         private object monitor = new object ();
         private AutoResetEvent thread_wait;
         private bool processing = false;
         private bool threaded = true;
         private bool canceled = false;
+
+        private int processed_count;
+        public int ProcessedCount {
+            get { return processed_count; }
+        }
+        
+        private int total_count;
+        public int TotalCount {
+            get { return total_count; }
+        }
         
         protected abstract T ProcessItem (T item);
         
@@ -57,17 +68,32 @@
             lock (this) {
                 canceled = false;
             }
+
+            lock (queue) {
+                total_count = 0;
+                processed_count = 0;
+            }
             
             EventHandler handler = Finished;
             if (handler != null) {
                 handler (this, EventArgs.Empty);
             }
         }
+
+        protected void OnProcessedItem ()
+        {
+            EventHandler handler = ProcessedItem;
+            if (handler != null) {
+                handler (this, EventArgs.Empty);
+            }
+        }
         
         protected virtual void OnCanceled ()
         {
             lock (queue) {
                 queue.Clear ();
+                total_count = 0;
+                processed_count = 0;
             }
         }
         
@@ -76,6 +102,7 @@
             lock (this) {                
                 lock (queue) {
                     queue.Enqueue (item);
+                    total_count++;
                 }
                 
                 if (!threaded) {
@@ -120,15 +147,16 @@
                         T item = null;
                         lock (queue) {
                             item = queue.Dequeue ();
+                            processed_count++;
                         }
                         
                         EnqueueDownstream (ProcessItem (item));
+                        OnProcessedItem ();
                     }
                 } catch (ElementProcessCanceledException) {
                     OnCanceled ();
                 }
-                
-                
+
                 lock (this) {
                     processing = false;
                 }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs	Fri Sep 12 01:35:43 2008
@@ -383,13 +383,18 @@
         
         public IEnumerable<T> FetchAllMatching (string condition, params object [] vals)
         {
-            HyenaSqliteCommand fetch_matching_command = new HyenaSqliteCommand (String.Format ("{0} AND {1}", SelectCommand.Text, condition));
+            HyenaSqliteCommand fetch_matching_command = CreateFetchCommand (condition);
             using (IDataReader reader = connection.Query (fetch_matching_command, vals)) {
                 while (reader.Read ()) {
                     yield return Load (reader);
                 }
             }
         }
+
+        public HyenaSqliteCommand CreateFetchCommand (string condition)
+        {
+            return new HyenaSqliteCommand (String.Format ("{0} AND {1}", SelectCommand.Text, condition));
+        }
         
         public IEnumerable<T> FetchRange (int offset, int limit)
         {



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