Index: src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.pidb
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.pidb
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.addin.xml
===================================================================
--- src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.addin.xml	(revision 0)
+++ src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.addin.xml	(revision 0)
@@ -0,0 +1,16 @@
+<Addin  id="Banshee.LibraryWatcher"
+        name="Library Watcher"
+        description="Automatically update music and video libraries"
+        url=""
+	author="Christian Martellini Alex Hixon"
+        version="1.0">
+
+  <Dependencies>
+    <Addin id="Banshee.Services" version="1.0"/>
+  </Dependencies>
+
+  <Extension path="/Banshee/ServiceManager/Service">
+    <Service class="Banshee.Plugins.LibraryWatcher.LibraryWatcherService"/>
+  </Extension>
+  
+</Addin>
Index: src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.csproj
===================================================================
--- src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.csproj	(revision 0)
+++ src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher.csproj	(revision 0)
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.50727</ProductVersion>
+    <ProjectGuid>{F38B53BA-8F85-4DC6-9B94-029C1CF96F24}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <UseParentDirectoryAsNamespace>true</UseParentDirectoryAsNamespace>
+    <AssemblyName>Banshee.LibraryWatcher</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>..\..\..\bin</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+    <CustomCommands>
+      <CustomCommands>
+        <Command type="Build" command="make" workingdir="${SolutionDir}" />
+        <Command type="Execute" command="make run" workingdir="${SolutionDir}" />
+      </CustomCommands>
+    </CustomCommands>
+    <AssemblyKeyFile>.</AssemblyKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Banshee.Core\Banshee.Core.csproj">
+      <Project>{2ADB831A-A050-47D0-B6B9-9C19D60233BB}</Project>
+      <Name>Banshee.Core</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Core\Banshee.Services\Banshee.Services.csproj">
+      <Project>{B28354F0-BA87-44E8-989F-B864A3C7C09F}</Project>
+      <Name>Banshee.Services</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Libraries\Hyena\Hyena.csproj">
+      <Project>{95374549-9553-4C1E-9D89-667755F90E12}</Project>
+      <Name>Hyena</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Core\Banshee.ThickClient\Banshee.ThickClient.csproj">
+      <Project>{AC839523-7BDF-4AB6-8115-E17921B96EC6}</Project>
+      <Name>Banshee.ThickClient</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="Mono.Addins, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
+    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Banshee.LibraryWacher.addin.xml" />
+    <EmbeddedResource Include="Resources\ActiveSourceUI.xml">
+      <LogicalName>ActiveSourceUI.xml</LogicalName>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Resources\GlobalUI.xml">
+      <LogicalName>GlobalUI.xml</LogicalName>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Banshee.LibraryWatcher\LibraryWatcherService.cs" />
+    <Compile Include="Banshee.LibraryWatcher\SourceWatcher.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ProjectExtensions>
+    <MonoDevelop>
+      <Properties>
+        <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="./Makefile.am">
+          <BuildFilesVar Sync="true" Name="SOURCES" />
+          <DeployFilesVar />
+          <ResourcesVar Sync="true" Name="RESOURCES" />
+          <OthersVar />
+          <GacRefVar />
+          <AsmRefVar />
+          <ProjectRefVar />
+        </MonoDevelop.Autotools.MakefileInfo>
+      </Properties>
+    </MonoDevelop>
+  </ProjectExtensions>
+</Project>
Index: src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/SourceWatcher.cs
===================================================================
--- src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/SourceWatcher.cs	(revision 0)
+++ src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/SourceWatcher.cs	(revision 0)
@@ -0,0 +1,209 @@
+using System;
+using System.IO;
+using System.Data;
+using System.Threading;
+using System.Collections.Generic;
+
+using Banshee;
+using Banshee.Base;
+using Banshee.Library;
+using Banshee.Sources;
+using Banshee.ServiceStack;
+using Banshee.Collection.Database;
+
+using Hyena;
+using Hyena.Data.Sqlite;
+
+namespace Banshee.Plugins.LibraryWatcher
+{	
+	
+	public class SourceWatcher
+	{
+		private   LibraryImportManager import_manager;
+		
+		private   bool   forceUpdate;
+		private   string dir_to_watch;
+		
+		/*sources to refresh when something change*/
+		public	  PrimarySource MusicLibrary;
+		public	  PrimarySource VideoLibrary;
+		
+		private   FileSystemWatcher watcher  = new FileSystemWatcher();
+				
+		private   List<string> tracks_to_remove = new List<string>(); 	 /*songs to remove list*/
+		private   List<string> tracks_to_add 	= new List<string>();    /*songs to add list*/
+		
+		private   Thread watchThread;
+	
+		public SourceWatcher(string dir_to_watch)
+		{
+			import_manager = ServiceManager.Get<LibraryImportManager>();
+			import_manager.KeepUserJobHidden = true;
+			
+			this.dir_to_watch = dir_to_watch;
+		}
+
+#region Public Methods
+		public void StartWatch()
+		{	
+			watcher.Path     = dir_to_watch; 
+       		watcher.Created += new FileSystemEventHandler(OnFileSystemEvent);
+       		watcher.Deleted += new FileSystemEventHandler(OnFileSystemEvent);
+			watcher.Renamed += new RenamedEventHandler(OnRenamed);
+			
+            watcher.IncludeSubdirectories = true;
+            watcher.EnableRaisingEvents   = true;
+			
+			/*the thread that make the job*/
+			watchThread = new Thread(new ThreadStart(watch));
+			watchThread.Start();
+			
+			Log.Debug("watching dir {0}",dir_to_watch);
+		}
+		
+		public void StopWatch()
+		{
+			watcher.Created -= new FileSystemEventHandler(OnFileSystemEvent);
+			watcher.Deleted -= new FileSystemEventHandler(OnFileSystemEvent);
+			watcher.Renamed -= new RenamedEventHandler(OnRenamed);
+			watcher.EnableRaisingEvents = false;
+			
+			watchThread.Abort();
+		}
+
+#endregion
+
+#region Private Methods
+		private void OnRenamed (object source, RenamedEventArgs e)
+        {
+           forceUpdate = true;
+        }
+
+		private void OnFileSystemEvent (object source, FileSystemEventArgs e)
+        {
+            if (e.ChangeType == WatcherChangeTypes.Created) {
+				lock(tracks_to_add){
+					tracks_to_add.Add(e.FullPath);
+				}
+            } else if (e.ChangeType == WatcherChangeTypes.Deleted) {
+                lock(tracks_to_remove){
+					tracks_to_remove.Add(e.FullPath);
+				}
+            }
+        }
+		
+		private void watch()
+        {
+        	/*update the library at startup*/
+        	forceUpdate = true;
+			
+			/*the main thread*/
+			while(true) {
+				bool needReload = false;
+				if (forceUpdate) {
+					SearchForDeletedSongs();
+					SearchForNewSongs();
+					forceUpdate = false;
+				}
+								
+				if (tracks_to_remove.Count > 0) {
+					lock(tracks_to_remove){
+						RemoveFromLibrary(tracks_to_remove);
+					}
+					needReload = true;
+				}
+				
+				if (tracks_to_add.Count > 0) {
+					lock(tracks_to_add){
+						AddToLibrary(tracks_to_add);
+					}
+					needReload = true;
+				}
+				
+				if (needReload) {
+					this.MusicLibrary.Reload();
+					this.VideoLibrary.Reload();
+					this.MusicLibrary.NotifyTracksChanged();
+					this.VideoLibrary.NotifyTracksChanged();
+				}
+				Thread.Sleep(400);
+			}
+        }
+		
+		private void AddToLibrary(List<string> tracks)
+		{
+			foreach(String track in tracks){
+				import_manager.ImportTrack(track);
+			}
+			tracks.Clear();
+		}
+		
+		private void RemoveFromLibrary(List<string> tracks) {
+			foreach(String track in tracks){
+				RemoveTrack(track);
+			}
+			tracks.Clear();
+		}
+		
+		private void RemoveTrack(string track)
+		{
+            string relative_path = Paths.MakePathRelative(new SafeUri(track).AbsolutePath , dir_to_watch);
+            int[] source_ids =  new int [] { this.MusicLibrary.DbId, this.VideoLibrary.DbId };
+            int track_id = DatabaseTrackInfo.GetTrackIdForUri (relative_path, source_ids);
+            HyenaSqliteCommand delete_command;
+            if (track_id > 0) {
+            	delete_command = new  HyenaSqliteCommand(string.Format("BEGIN;DELETE FROM CoreTracks WHERE TrackID = '{0}';COMMIT ",track_id));
+            }else {
+            	/*if track_id is 0 you removed a directory*/
+            	delete_command = new  HyenaSqliteCommand(string.Format("BEGIN;DELETE FROM CoreTracks WHERE TrackID IN (SELECT TrackID FROM CoreTracks where lower(Uri) LIKE '{0}%');COMMIT",relative_path + "/"));
+            }
+            ServiceManager.DbConnection.Execute(@delete_command);
+		}
+		
+		private void SearchForDeletedSongs()
+		{	
+			SafeUri  uri;
+			HyenaSqliteCommand select_command = new HyenaSqliteCommand ("SELECT Uri from CoreTracks");
+			IDataReader reader = ServiceManager.DbConnection.Query(select_command);
+			while(reader.Read()){				
+				string   uri_str = (string)reader["Uri"];
+				if (uri_str.StartsWith("file:///") || uri_str.Contains(dir_to_watch)){
+					uri = new SafeUri(uri_str);
+				}else{
+					uri = new SafeUri(dir_to_watch + "/" + uri_str); 
+				}
+				if (!Banshee.IO.File.Exists(uri)){
+					tracks_to_remove.Add(uri.AbsolutePath);
+				}
+			}
+		}
+		
+		private void SearchForNewSongs()
+		{
+			string[] filenames = System.IO.Directory.GetFiles(dir_to_watch,"*", SearchOption.AllDirectories);
+			foreach (string filename in filenames){
+				if (IsValidPath(filename)){
+					tracks_to_add.Add(filename);	
+				}
+			}
+		}
+		
+		private bool IsValidPath(string track_path)
+		{
+			string relative_path = Paths.MakePathRelative(track_path , dir_to_watch);
+			if (System.IO.Directory.Exists(track_path)){
+				return false;
+			}
+			if (!DatabaseImportManager.IsWhiteListedFile(track_path)){
+				return false;
+			}
+			int[] source_ids =  new int [] { this.MusicLibrary.DbId, this.VideoLibrary.DbId };
+			if (DatabaseTrackInfo.ContainsUri(relative_path,source_ids)){
+				return false;
+			}
+
+			return true;
+		}
+#endregion
+	}
+}
Index: src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs
===================================================================
--- src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs	(revision 0)
+++ src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs	(revision 0)
@@ -0,0 +1,81 @@
+using System;
+using Hyena;
+
+using Banshee;
+using Banshee.Base ;
+using Banshee.Sources;
+using Banshee.ServiceStack;
+using Banshee.Library;
+
+namespace Banshee.Plugins.LibraryWatcher
+{	
+	public class LibraryWatcherService : IExtensionService, IDisposable
+	{
+		private bool watcher_started = false;
+		private static SourceWatcher watcher = new SourceWatcher(Paths.CachedLibraryLocation);
+		
+		string IService.ServiceName
+        {
+            get { return "Lybrary Watcher Plugin"; }
+        }
+		
+		void IExtensionService.Initialize ()
+		{
+			Log.Debug("Init library watcher plugin...");
+			try {
+                 if (!ServiceStarted ()) {
+                    ServiceManager.ServiceStarted += OnServiceStarted;
+                 }else {
+                 	this.StartWatch();
+                 }
+             } catch (Exception ex) {
+                Log.Error("Error initializing library watcher: " + ex.Message);
+			 }
+		}
+		
+		public void Dispose ()
+        {
+			ServiceManager.SourceManager.SourceAdded -= OnSourceAdded;
+			if (watcher != null){
+				watcher.StopWatch();
+			}
+			watcher_started = false;
+        }
+
+		private bool ServiceStarted ()
+        {
+			if (!ServiceManager.IsInitialized || 
+			    ServiceManager.Get<LibraryImportManager>() == null ||
+			    ServiceManager.SourceManager.MusicLibrary  == null ||
+			    ServiceManager.SourceManager.VideoLibrary  == null) {
+				return false;
+			}			
+            return true;
+        }
+
+		private void OnSourceAdded(SourceEventArgs evt)
+		{
+			if (evt.Source is MusicLibrarySource) {
+			 	watcher.MusicLibrary = (PrimarySource)evt.Source;
+			}
+			if (evt.Source is VideoLibrarySource){
+				watcher.VideoLibrary = (PrimarySource)evt.Source;
+			}
+			
+			if(!watcher_started && watcher.MusicLibrary != null && watcher.VideoLibrary != null) {
+				this.StartWatch();
+			}
+		}
+		
+		private void StartWatch()
+		{	
+			watcher.StartWatch();
+			watcher_started = true;
+		}
+		
+		private void OnServiceStarted(ServiceStartedArgs args)
+		{
+            ServiceManager.SourceManager.SourceAdded += OnSourceAdded;
+		}
+	}
+}
Index: src/Extensions/Banshee.LibraryWatcher/Makefile.am
===================================================================
--- src/Extensions/Banshee.LibraryWatcher/Makefile.am	(revision 0)
+++ src/Extensions/Banshee.LibraryWatcher/Makefile.am	(revision 0)
@@ -0,0 +1,15 @@
+ASSEMBLY = Banshee.LibraryWatcher
+TARGET = library
+LINK = $(REF_EXTENSION_LIBRARYWATCHER)
+INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
+
+SOURCES =  \
+	Banshee.LibraryWatcher/LibraryWatcherService.cs \
+	Banshee.LibraryWatcher/SourceWatcher.cs
+	
+
+RESOURCES =  \
+	Banshee.LibraryWatcher.addin.xml
+
+include $(top_srcdir)/build/build.mk
+
Index: src/Extensions/Makefile.am
===================================================================
--- src/Extensions/Makefile.am	(revision 5209)
+++ src/Extensions/Makefile.am	(working copy)
@@ -20,6 +20,6 @@
 	Banshee.Sample \
 	Banshee.SqlDebugConsole \
 	Banshee.Torrent \
-	Banshee.RemoteAudio
-
+	Banshee.RemoteAudio \
+	Banshee.LibraryWatcher
 MAINTAINERCLEANFILES = Makefile.in
Index: build/build.environment.mk
===================================================================
--- build/build.environment.mk	(revision 5209)
+++ build/build.environment.mk	(working copy)
@@ -116,6 +116,7 @@
 REF_DAP_KARMA = $(LINK_DAP_DEPS) $(LINK_KARMA_DEPS)
 
 # Extensions
+REF_EXTENSION_LIBRARYWATCHER = $(LINK_BANSHEE_SERVICES_DEPS)
 REF_EXTENSION_AUDIOCD = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_MUSICBRAINZ_DEPS)
 REF_EXTENSION_BOOKMARKS = $(LINK_BANSHEE_THICKCLIENT_DEPS)
 REF_EXTENSION_BOOSCRIPT = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_BOO)
Index: configure.ac
===================================================================
--- configure.ac	(revision 5209)
+++ configure.ac	(working copy)
@@ -251,6 +251,7 @@
 src/Extensions/Banshee.Sample/Makefile
 src/Extensions/Banshee.Torrent/Makefile
 src/Extensions/Banshee.RemoteAudio/Makefile
+src/Extensions/Banshee.LibraryWatcher/Makefile
 ])
 
 cat <<EOF
