using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.IO { public class DirectoryWatchers { private List FileSystemWatchers = new List(); private Timer updateTimer = null; private List affectedPaths = new List(); private const int TimerDelayInSeconds = 5; public void Start() { List pathsToWatch = new List(); var rootFolder = Kernel.Instance.RootFolder; pathsToWatch.Add(rootFolder.Path); foreach (Folder folder in rootFolder.Children.OfType()) { foreach (Folder subFolder in folder.Children.OfType()) { if (Path.IsPathRooted(subFolder.Path)) { string parent = Path.GetDirectoryName(subFolder.Path); if (!pathsToWatch.Contains(parent)) { pathsToWatch.Add(parent); } } } } foreach (string path in pathsToWatch) { FileSystemWatcher watcher = new FileSystemWatcher(path, "*"); watcher.IncludeSubdirectories = true; watcher.Changed += watcher_Changed; // All the others seem to trigger change events on the parent, so let's keep it simple for now. //watcher.Created += watcher_Changed; //watcher.Deleted += watcher_Changed; //watcher.Renamed += watcher_Changed; watcher.EnableRaisingEvents = true; FileSystemWatchers.Add(watcher); } } void watcher_Changed(object sender, FileSystemEventArgs e) { if (!affectedPaths.Contains(e.FullPath)) { affectedPaths.Add(e.FullPath); } if (updateTimer == null) { updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(TimerDelayInSeconds), TimeSpan.FromMilliseconds(-1)); } else { updateTimer.Change(TimeSpan.FromSeconds(TimerDelayInSeconds), TimeSpan.FromMilliseconds(-1)); } } private void TimerStopped(object stateInfo) { updateTimer.Dispose(); updateTimer = null; List paths = affectedPaths; affectedPaths = new List(); ProcessPathChanges(paths); } private void ProcessPathChanges(IEnumerable paths) { List itemsToRefresh = new List(); foreach (BaseItem item in paths.Select(p => GetAffectedBaseItem(p))) { if (item != null && !itemsToRefresh.Contains(item)) { itemsToRefresh.Add(item); } } if (itemsToRefresh.Any(i => { var folder = i as Folder; return folder != null && folder.IsRoot; })) { Kernel.Instance.ReloadRoot(); } else { Parallel.For(0, itemsToRefresh.Count, i => { Kernel.Instance.ReloadItem(itemsToRefresh[i]); }); } } private BaseItem GetAffectedBaseItem(string path) { BaseItem item = null; while (item == null) { item = Kernel.Instance.RootFolder.FindByPath(path); path = Path.GetDirectoryName(path); } return item; } public void Stop() { foreach (FileSystemWatcher watcher in FileSystemWatchers) { watcher.Changed -= watcher_Changed; watcher.EnableRaisingEvents = false; watcher.Dispose(); } if (updateTimer != null) { updateTimer.Dispose(); updateTimer = null; } FileSystemWatchers.Clear(); affectedPaths.Clear(); } } }