diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 984584e9a5..8bbc942725 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.IO; using System; +using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; @@ -179,5 +180,14 @@ namespace MediaBrowser.Controller.Entities data.PlaybackPositionTicks = 0; } } + + /// + /// Do whatever refreshing is necessary when the filesystem pertaining to this item has changed. + /// + /// + public virtual Task ChangedExternally() + { + return Task.Run(() => RefreshMetadata()); + } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 7d58e5e20b..6cf47c2f2d 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -272,7 +272,7 @@ namespace MediaBrowser.Controller.Entities } /// - /// Finds child BaseItems for a given Folder + /// Finds child BaseItems for us /// protected Task[] GetChildren(WIN32_FIND_DATA[] fileSystemChildren) { @@ -328,6 +328,26 @@ namespace MediaBrowser.Controller.Entities } } + /// + /// Folders need to validate and refresh + /// + /// + public override Task ChangedExternally() + { + return Task.Run(() => + { + if (this.IsRoot) + { + Kernel.Instance.ReloadRoot().ConfigureAwait(false); + } + else + { + RefreshMetadata(); + ValidateChildren(); + } + }); + } + /// /// Since it can be slow to make all of these calculations at once, this method will provide a way to get them all back together /// @@ -578,17 +598,8 @@ namespace MediaBrowser.Controller.Entities return result; } - foreach (BaseItem item in ActualChildren) - { - result = item.FindItemById(id); - - if (result != null) - { - return result; - } - } - - return null; + //this should be functionally equivilent to what was here since it is IEnum and works on a thread-safe copy + return RecursiveChildren.FirstOrDefault(i => i.Id == id); } /// @@ -596,31 +607,13 @@ namespace MediaBrowser.Controller.Entities /// public BaseItem FindByPath(string path) { - if (Path.Equals(path, StringComparison.OrdinalIgnoreCase)) + if (PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)) { return this; } - foreach (BaseItem item in ActualChildren) - { - var folder = item as Folder; - - if (folder != null) - { - var foundItem = folder.FindByPath(path); - - if (foundItem != null) - { - return foundItem; - } - } - else if (item.Path.Equals(path, StringComparison.OrdinalIgnoreCase)) - { - return item; - } - } - - return null; + //this should be functionally equivilent to what was here since it is IEnum and works on a thread-safe copy + return RecursiveChildren.FirstOrDefault(i => i.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); } } } diff --git a/MediaBrowser.Controller/IO/DirectoryWatchers.cs b/MediaBrowser.Controller/IO/DirectoryWatchers.cs index 8243c8174a..2e33400892 100644 --- a/MediaBrowser.Controller/IO/DirectoryWatchers.cs +++ b/MediaBrowser.Controller/IO/DirectoryWatchers.cs @@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.IO private Timer updateTimer; private List affectedPaths = new List(); - private const int TimerDelayInSeconds = 5; + private const int TimerDelayInSeconds = 30; public void Start() { @@ -44,12 +44,13 @@ namespace MediaBrowser.Controller.IO var watcher = new FileSystemWatcher(path, "*") { }; watcher.IncludeSubdirectories = true; - watcher.Changed += watcher_Changed; + //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; + // 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; watcher.EnableRaisingEvents = true; FileSystemWatchers.Add(watcher); @@ -58,9 +59,23 @@ namespace MediaBrowser.Controller.IO void watcher_Changed(object sender, FileSystemEventArgs e) { - if (!affectedPaths.Contains(e.FullPath)) + Logger.LogDebugInfo("****** Watcher sees change of type " + e.ChangeType.ToString() + " to " + e.FullPath); + lock (affectedPaths) { - affectedPaths.Add(e.FullPath); + if (!affectedPaths.Contains(e.FullPath)) + { + Logger.LogDebugInfo("****** Adding " + e.FullPath + " to affected paths."); + affectedPaths.Add(e.FullPath); + } + 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); + } + } } if (updateTimer == null) @@ -77,9 +92,12 @@ namespace MediaBrowser.Controller.IO { updateTimer.Dispose(); updateTimer = null; - - List paths = affectedPaths; - affectedPaths = new List(); + List paths; + lock (affectedPaths) + { + paths = affectedPaths; + affectedPaths = new List(); + } await ProcessPathChanges(paths).ConfigureAwait(false); } @@ -106,14 +124,16 @@ namespace MediaBrowser.Controller.IO return Kernel.Instance.ReloadRoot(); } - return Task.WhenAll(itemsToRefresh.Select(i => Kernel.Instance.ReloadItem(i))); + foreach (var p in paths) Logger.LogDebugInfo("********* "+ p + " reports change."); + foreach (var i in itemsToRefresh) Logger.LogDebugInfo("********* "+i.Name + " will be refreshed."); + return Task.WhenAll(itemsToRefresh.Select(i => i.ChangedExternally())); } private BaseItem GetAffectedBaseItem(string path) { BaseItem item = null; - while (item == null) + while (item == null && !string.IsNullOrEmpty(path)) { item = Kernel.Instance.RootFolder.FindByPath(path); diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index c614737846..503935eed7 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -201,10 +201,10 @@ namespace MediaBrowser.Controller //Task.Delay(30000); //let's wait and see if more data gets filled in... var allChildren = RootFolder.RecursiveChildren; Logger.LogDebugInfo(string.Format("Loading complete. Movies: {0} Episodes: {1} Folders: {2}", allChildren.OfType().Count(), allChildren.OfType().Count(), allChildren.Where(i => i is Folder && !(i is Series || i is Season)).Count())); - foreach (var child in allChildren) - { - Logger.LogDebugInfo("(" + child.GetType().Name + ") " + child.Name + " (" + child.Path + ")"); - } + //foreach (var child in allChildren) + //{ + // Logger.LogDebugInfo("(" + child.GetType().Name + ") " + child.Name + " (" + child.Path + ")"); + //} } /// @@ -257,36 +257,6 @@ namespace MediaBrowser.Controller return result; } - public async Task ReloadItem(BaseItem item) - { - var folder = item as Folder; - - if (folder != null && folder.IsRoot) - { - await ReloadRoot().ConfigureAwait(false); - } - else - { - if (!Directory.Exists(item.Path) && !File.Exists(item.Path)) - { - await ReloadItem(item.Parent).ConfigureAwait(false); - return; - } - - BaseItem newItem = await ItemController.GetItem(item.Path, item.Parent).ConfigureAwait(false); - - List children = item.Parent.Children.ToList(); - - int index = children.IndexOf(item); - - children.RemoveAt(index); - - children.Insert(index, newItem); - - //item.Parent.ActualChildren = children.ToArray(); - } - } - /// /// Finds a library item by Id /// diff --git a/MediaBrowser.Controller/Library/ItemController.cs b/MediaBrowser.Controller/Library/ItemController.cs index c98e22ba19..54673e538d 100644 --- a/MediaBrowser.Controller/Library/ItemController.cs +++ b/MediaBrowser.Controller/Library/ItemController.cs @@ -13,21 +13,6 @@ namespace MediaBrowser.Controller.Library { public class ItemController { - //private BaseItem ResolveItem(ItemResolveEventArgs args) - //{ - // // Try first priority resolvers - // for (int i = 0; i < Kernel.Instance.EntityResolvers.Length; i++) - // { - // var item = Kernel.Instance.EntityResolvers[i].ResolvePath(args); - - // if (item != null) - // { - // return item; - // } - // } - - // return null; - //} /// /// Resolves a path into a BaseItem @@ -56,7 +41,7 @@ namespace MediaBrowser.Controller.Library } - // Fire BeginResolvePath to see if anyone wants to cancel this operation + // Check to see if we should resolve based on our contents if (!EntityResolutionHelper.ShouldResolvePathContents(args)) { return null; @@ -67,80 +52,6 @@ namespace MediaBrowser.Controller.Library return item; } - ///// - ///// Finds child BaseItems for a given Folder - ///// - //private Task[] GetChildren(Folder folder, WIN32_FIND_DATA[] fileSystemChildren, bool allowInternetProviders) - //{ - // Task[] tasks = new Task[fileSystemChildren.Length]; - - // for (int i = 0; i < fileSystemChildren.Length; i++) - // { - // var child = fileSystemChildren[i]; - - // tasks[i] = GetItem(child.Path, folder, child, allowInternetProviders: allowInternetProviders); - // } - - // return tasks; - //} - - ///// - ///// Transforms shortcuts into their actual paths - ///// - //private WIN32_FIND_DATA[] FilterChildFileSystemEntries(WIN32_FIND_DATA[] fileSystemChildren, bool flattenShortcuts) - //{ - // WIN32_FIND_DATA[] returnArray = new WIN32_FIND_DATA[fileSystemChildren.Length]; - // List resolvedShortcuts = new List(); - - // for (int i = 0; i < fileSystemChildren.Length; i++) - // { - // WIN32_FIND_DATA file = fileSystemChildren[i]; - - // // If it's a shortcut, resolve it - // if (Shortcut.IsShortcut(file.Path)) - // { - // string newPath = Shortcut.ResolveShortcut(file.Path); - // WIN32_FIND_DATA newPathData = FileData.GetFileData(newPath); - - // // Find out if the shortcut is pointing to a directory or file - // if (newPathData.IsDirectory) - // { - // // If we're flattening then get the shortcut's children - - // if (flattenShortcuts) - // { - // returnArray[i] = file; - // WIN32_FIND_DATA[] newChildren = FileData.GetFileSystemEntries(newPath, "*").ToArray(); - - // resolvedShortcuts.AddRange(FilterChildFileSystemEntries(newChildren, false)); - // } - // else - // { - // returnArray[i] = newPathData; - // } - // } - // else - // { - // returnArray[i] = newPathData; - // } - // } - // else - // { - // returnArray[i] = file; - // } - // } - - // if (resolvedShortcuts.Count > 0) - // { - // resolvedShortcuts.InsertRange(0, returnArray); - // return resolvedShortcuts.ToArray(); - // } - // else - // { - // return returnArray; - // } - //} - /// /// Gets a Person ///