2012-09-10 21:34:02 -04:00
|
|
|
|
using MediaBrowser.Controller.Entities;
|
2012-09-17 11:12:43 -04:00
|
|
|
|
using MediaBrowser.Common.Logging;
|
|
|
|
|
using MediaBrowser.Common.Extensions;
|
2012-09-10 21:34:02 -04:00
|
|
|
|
using System;
|
2012-07-12 02:55:27 -04:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace MediaBrowser.Controller.IO
|
|
|
|
|
{
|
|
|
|
|
public class DirectoryWatchers
|
|
|
|
|
{
|
2012-09-11 14:20:12 -04:00
|
|
|
|
private readonly List<FileSystemWatcher> FileSystemWatchers = new List<FileSystemWatcher>();
|
|
|
|
|
private Timer updateTimer;
|
2012-07-12 02:55:27 -04:00
|
|
|
|
private List<string> affectedPaths = new List<string>();
|
|
|
|
|
|
2012-09-20 11:25:16 -04:00
|
|
|
|
private const int TimerDelayInSeconds = 30;
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
2012-09-11 15:37:14 -04:00
|
|
|
|
var pathsToWatch = new List<string>();
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
|
|
|
|
var rootFolder = Kernel.Instance.RootFolder;
|
|
|
|
|
|
|
|
|
|
pathsToWatch.Add(rootFolder.Path);
|
|
|
|
|
|
2012-07-19 22:22:44 -04:00
|
|
|
|
foreach (Folder folder in rootFolder.Children.OfType<Folder>())
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
2012-09-17 11:12:43 -04:00
|
|
|
|
foreach (string path in folder.PhysicalLocations)
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
2012-09-18 07:22:49 -04:00
|
|
|
|
if (Path.IsPathRooted(path) && !pathsToWatch.ContainsParentFolder(path))
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
2012-09-17 11:12:43 -04:00
|
|
|
|
pathsToWatch.Add(path);
|
2012-07-12 02:55:27 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (string path in pathsToWatch)
|
|
|
|
|
{
|
2012-09-17 11:12:43 -04:00
|
|
|
|
Logger.LogInfo("Watching directory " + path + " for changes.");
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
2012-09-17 16:53:39 -04:00
|
|
|
|
var watcher = new FileSystemWatcher(path, "*") { };
|
2012-07-12 02:55:27 -04:00
|
|
|
|
watcher.IncludeSubdirectories = true;
|
|
|
|
|
|
2012-09-20 11:25:16 -04:00
|
|
|
|
//watcher.Changed += watcher_Changed;
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
|
|
|
|
// All the others seem to trigger change events on the parent, so let's keep it simple for now.
|
2012-09-20 11:25:16 -04:00
|
|
|
|
// Actually, we really need to only watch created, deleted and renamed as changed fires too much -ebr
|
|
|
|
|
watcher.Created += watcher_Changed;
|
|
|
|
|
watcher.Deleted += watcher_Changed;
|
|
|
|
|
watcher.Renamed += watcher_Changed;
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
|
|
|
|
watcher.EnableRaisingEvents = true;
|
|
|
|
|
FileSystemWatchers.Add(watcher);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void watcher_Changed(object sender, FileSystemEventArgs e)
|
|
|
|
|
{
|
2012-09-20 11:25:16 -04:00
|
|
|
|
Logger.LogDebugInfo("****** Watcher sees change of type " + e.ChangeType.ToString() + " to " + e.FullPath);
|
|
|
|
|
lock (affectedPaths)
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
2012-09-20 13:53:10 -04:00
|
|
|
|
//Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path
|
|
|
|
|
var affectedPath = Path.GetDirectoryName(e.FullPath);
|
|
|
|
|
|
2012-09-20 11:25:16 -04:00
|
|
|
|
if (e.ChangeType == WatcherChangeTypes.Renamed)
|
|
|
|
|
{
|
|
|
|
|
var renamedArgs = e as RenamedEventArgs;
|
|
|
|
|
if (affectedPaths.Contains(renamedArgs.OldFullPath))
|
|
|
|
|
{
|
|
|
|
|
Logger.LogDebugInfo("****** Removing " + renamedArgs.OldFullPath + " from affected paths.");
|
|
|
|
|
affectedPaths.Remove(renamedArgs.OldFullPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-20 13:53:10 -04:00
|
|
|
|
|
|
|
|
|
//If anything underneath this path was already marked as affected - remove it as it will now get captured by this one
|
|
|
|
|
affectedPaths.RemoveAll(p => p.StartsWith(e.FullPath, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (!affectedPaths.ContainsParentFolder(affectedPath))
|
|
|
|
|
{
|
|
|
|
|
Logger.LogDebugInfo("****** Adding " + affectedPath + " to affected paths.");
|
|
|
|
|
affectedPaths.Add(affectedPath);
|
|
|
|
|
}
|
2012-07-12 02:55:27 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (updateTimer == null)
|
|
|
|
|
{
|
|
|
|
|
updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(TimerDelayInSeconds), TimeSpan.FromMilliseconds(-1));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
updateTimer.Change(TimeSpan.FromSeconds(TimerDelayInSeconds), TimeSpan.FromMilliseconds(-1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-19 16:38:31 -04:00
|
|
|
|
private async void TimerStopped(object stateInfo)
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
|
|
|
|
updateTimer.Dispose();
|
|
|
|
|
updateTimer = null;
|
2012-09-20 11:25:16 -04:00
|
|
|
|
List<string> paths;
|
|
|
|
|
lock (affectedPaths)
|
|
|
|
|
{
|
|
|
|
|
paths = affectedPaths;
|
|
|
|
|
affectedPaths = new List<string>();
|
|
|
|
|
}
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
2012-08-20 19:53:32 -04:00
|
|
|
|
await ProcessPathChanges(paths).ConfigureAwait(false);
|
2012-07-12 02:55:27 -04:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-21 22:50:59 -04:00
|
|
|
|
private Task ProcessPathChanges(IEnumerable<string> paths)
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
2012-09-11 15:37:14 -04:00
|
|
|
|
var itemsToRefresh = new List<BaseItem>();
|
2012-07-12 02:55:27 -04:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}))
|
|
|
|
|
{
|
2012-08-21 22:50:59 -04:00
|
|
|
|
return Kernel.Instance.ReloadRoot();
|
2012-07-12 02:55:27 -04:00
|
|
|
|
}
|
2012-09-11 14:20:12 -04:00
|
|
|
|
|
2012-09-20 11:25:16 -04:00
|
|
|
|
foreach (var p in paths) Logger.LogDebugInfo("********* "+ p + " reports change.");
|
2012-09-20 13:53:10 -04:00
|
|
|
|
foreach (var i in itemsToRefresh) Logger.LogDebugInfo("********* "+i.Name + " ("+ i.Path + ") will be refreshed.");
|
2012-09-20 11:25:16 -04:00
|
|
|
|
return Task.WhenAll(itemsToRefresh.Select(i => i.ChangedExternally()));
|
2012-07-12 02:55:27 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BaseItem GetAffectedBaseItem(string path)
|
|
|
|
|
{
|
|
|
|
|
BaseItem item = null;
|
|
|
|
|
|
2012-09-20 11:25:16 -04:00
|
|
|
|
while (item == null && !string.IsNullOrEmpty(path))
|
2012-07-12 02:55:27 -04:00
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|