Browse Source

extracted provider manager. took more off the kernel

LukePulverenti 12 năm trước cách đây
mục cha
commit
9911df11e8
54 tập tin đã thay đổi với 755 bổ sung635 xóa
  1. 1 1
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 2 3
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  3. 5 4
      MediaBrowser.Controller/Entities/BaseItem.cs
  4. 3 3
      MediaBrowser.Controller/Entities/User.cs
  5. 0 120
      MediaBrowser.Controller/IO/FileSystemManager.cs
  6. 29 0
      MediaBrowser.Controller/IO/IDirectoryWatchers.cs
  7. 7 1
      MediaBrowser.Controller/IServerApplicationPaths.cs
  8. 21 153
      MediaBrowser.Controller/Kernel.cs
  9. 1 1
      MediaBrowser.Controller/Localization/LocalizedStrings.cs
  10. 1 1
      MediaBrowser.Controller/Localization/Ratings.cs
  11. 3 3
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  12. 5 132
      MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
  13. 1 1
      MediaBrowser.Controller/Providers/FolderProviderFromXml.cs
  14. 51 0
      MediaBrowser.Controller/Providers/IProviderManager.cs
  15. 6 1
      MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs
  16. 7 4
      MediaBrowser.Controller/Providers/MediaInfo/BaseFFProbeProvider.cs
  17. 6 1
      MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs
  18. 6 1
      MediaBrowser.Controller/Providers/MediaInfo/FFMpegVideoImageProvider.cs
  19. 33 0
      MediaBrowser.Controller/Providers/MetadataProviderPriority.cs
  20. 17 10
      MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs
  21. 19 11
      MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs
  22. 3 3
      MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs
  23. 1 1
      MediaBrowser.Controller/Providers/Movies/MovieProviderFromXml.cs
  24. 3 3
      MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs
  25. 8 5
      MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs
  26. 7 3
      MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs
  27. 10 7
      MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs
  28. 5 2
      MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs
  29. 5 3
      MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs
  30. 1 1
      MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs
  31. 1 1
      MediaBrowser.Controller/Providers/Music/MusicArtistProviderFromJson.cs
  32. 12 1
      MediaBrowser.Controller/Providers/SortNameProvider.cs
  33. 6 1
      MediaBrowser.Controller/Providers/TV/EpisodeImageFromMediaLocationProvider.cs
  34. 1 1
      MediaBrowser.Controller/Providers/TV/EpisodeProviderFromXml.cs
  35. 8 5
      MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs
  36. 7 4
      MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs
  37. 9 8
      MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs
  38. 16 12
      MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs
  39. 1 1
      MediaBrowser.Controller/Providers/TV/SeriesProviderFromXml.cs
  40. 36 35
      MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs
  41. 2 0
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  42. 158 43
      MediaBrowser.Server.Implementations/Providers/ProviderManager.cs
  43. 6 2
      MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs
  44. 27 1
      MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
  45. 2 2
      MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs
  46. 4 4
      MediaBrowser.ServerApplication/App.xaml.cs
  47. 124 19
      MediaBrowser.ServerApplication/ApplicationHost.cs
  48. 1 1
      MediaBrowser.ServerApplication/EntryPoints/NewItemNotifier.cs
  49. 41 0
      MediaBrowser.ServerApplication/EntryPoints/RefreshUsersMetadata.cs
  50. 1 1
      MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
  51. 1 1
      MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs
  52. 13 6
      MediaBrowser.ServerApplication/MainWindow.xaml.cs
  53. 8 7
      MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
  54. 3 0
      MediaBrowser.sln

+ 1 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -93,7 +93,7 @@ namespace MediaBrowser.Api.Playback
         /// <returns>System.String.</returns>
         protected string GetOutputFilePath(StreamState state)
         {
-            var folder = ApplicationPaths.FFMpegStreamCachePath;
+            var folder = ApplicationPaths.EncodedMediaCachePath;
             return Path.Combine(folder, GetCommandLineArguments("dummy\\dummy", state).GetMD5() + GetOutputFileExtension(state).ToLower());
         }
 

+ 2 - 3
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -77,7 +77,7 @@ namespace MediaBrowser.Common.Implementations
         /// <summary>
         /// The json serializer
         /// </summary>
-        protected readonly IJsonSerializer JsonSerializer = new JsonSerializer();
+        public readonly IJsonSerializer JsonSerializer = new JsonSerializer();
 
         /// <summary>
         /// The _XML serializer
@@ -524,8 +524,7 @@ namespace MediaBrowser.Common.Implementations
         /// <returns>Task.</returns>
         public async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
         {
-            var pkgManager = Resolve<IPackageManager>();
-            await pkgManager.InstallPackage(progress, package, cancellationToken).ConfigureAwait(false);
+            await PackageManager.InstallPackage(progress, package, cancellationToken).ConfigureAwait(false);
 
             EventHelper.QueueEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs<Version> { Argument = package.version }, Logger);
         }

+ 5 - 4
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -96,9 +96,10 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// The logger
         /// </summary>
-        protected static internal ILogger Logger { get; internal set; }
-        protected static internal ILibraryManager LibraryManager { get; internal set; }
-        protected static internal IServerConfigurationManager ConfigurationManager { get; internal set; }
+        public static ILogger Logger { get; set; }
+        public static ILibraryManager LibraryManager { get; set; }
+        public static IServerConfigurationManager ConfigurationManager { get; set; }
+        public static IProviderManager ProviderManager { get; set; }
 
         /// <summary>
         /// Returns a <see cref="System.String" /> that represents this instance.
@@ -652,7 +653,7 @@ namespace MediaBrowser.Controller.Entities
             LocalTrailers = null;
 
             // Refresh for the item
-            var itemRefreshTask = Kernel.Instance.ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
+            var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders);
 
             cancellationToken.ThrowIfCancellationRequested();
 

+ 3 - 3
MediaBrowser.Controller/Entities/User.cs

@@ -18,8 +18,8 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class User : BaseItem
     {
-        internal static IUserManager UserManager { get; set; }
-        internal static IXmlSerializer XmlSerializer { get; set; }
+        public static IUserManager UserManager { get; set; }
+        public static IXmlSerializer XmlSerializer { get; set; }
 
         /// <summary>
         /// The _root folder path
@@ -363,7 +363,7 @@ namespace MediaBrowser.Controller.Entities
                 ResolveArgs = null;
             }
 
-            var changed = await Kernel.Instance.ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false);
+            var changed = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false);
 
             if (changed || forceSave)
             {

+ 0 - 120
MediaBrowser.Controller/IO/FileSystemManager.cs

@@ -1,120 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.IO
-{
-    /// <summary>
-    /// This class will manage our file system watching and modifications.  Any process that needs to
-    /// modify the directories that the system is watching for changes should use the methods of
-    /// this class to do so.  This way we can have the watchers correctly respond to only external changes.
-    /// </summary>
-    public class FileSystemManager : IDisposable
-    {
-        /// <summary>
-        /// Gets or sets the directory watchers.
-        /// </summary>
-        /// <value>The directory watchers.</value>
-        private DirectoryWatchers DirectoryWatchers { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FileSystemManager" /> class.
-        /// </summary>
-        /// <param name="logManager">The log manager.</param>
-        /// <param name="taskManager">The task manager.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <param name="configurationManager">The configuration manager.</param>
-        public FileSystemManager(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager)
-        {
-            DirectoryWatchers = new DirectoryWatchers(logManager, taskManager, libraryManager, configurationManager);
-        }
-
-        /// <summary>
-        /// Start the directory watchers on our library folders
-        /// </summary>
-        public void StartWatchers()
-        {
-            DirectoryWatchers.Start();
-        }
-
-        /// <summary>
-        /// Saves to library filesystem.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="path">The path.</param>
-        /// <param name="dataToSave">The data to save.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public async Task SaveToLibraryFilesystem(BaseItem item, string path, Stream dataToSave, CancellationToken cancellationToken)
-        {
-            if (item == null)
-            {
-                throw new ArgumentNullException();
-            }
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentNullException();
-            }
-            if (dataToSave == null)
-            {
-                throw new ArgumentNullException();
-            }
-            if (cancellationToken == null)
-            {
-                throw new ArgumentNullException();
-            }
-     
-            cancellationToken.ThrowIfCancellationRequested();
-
-            //Tell the watchers to ignore
-            DirectoryWatchers.TemporarilyIgnore(path);
-
-            //Make the mod
-
-            dataToSave.Position = 0;
-
-            try
-            {
-                using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
-                {
-                    await dataToSave.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
-
-                    dataToSave.Dispose();
-
-                    // If this is ever used for something other than metadata we can add a file type param
-                    item.ResolveArgs.AddMetadataFile(path);
-                }
-            }
-            finally
-            {
-                //Remove the ignore
-                DirectoryWatchers.RemoveTempIgnore(path);
-            }
-        }
-
-        /// <summary>
-        /// Releases unmanaged and - optionally - managed resources.
-        /// </summary>
-        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool dispose)
-        {
-            if (dispose)
-            {
-                DirectoryWatchers.Dispose();
-            }
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-    }
-}

+ 29 - 0
MediaBrowser.Controller/IO/IDirectoryWatchers.cs

@@ -0,0 +1,29 @@
+using System;
+
+namespace MediaBrowser.Controller.IO
+{
+    public interface IDirectoryWatchers : IDisposable
+    {
+        /// <summary>
+        /// Add the path to our temporary ignore list.  Use when writing to a path within our listening scope.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        void TemporarilyIgnore(string path);
+
+        /// <summary>
+        /// Removes the temp ignore.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        void RemoveTempIgnore(string path);
+
+        /// <summary>
+        /// Starts this instance.
+        /// </summary>
+        void Start();
+
+        /// <summary>
+        /// Stops this instance.
+        /// </summary>
+        void Stop();
+    }
+}

+ 7 - 1
MediaBrowser.Controller/IServerApplicationPaths.cs

@@ -74,12 +74,18 @@ namespace MediaBrowser.Controller
         /// Gets the FF MPEG stream cache path.
         /// </summary>
         /// <value>The FF MPEG stream cache path.</value>
-        string FFMpegStreamCachePath { get; }
+        string EncodedMediaCachePath { get; }
 
         /// <summary>
         /// Gets the folder path to tools
         /// </summary>
         /// <value>The media tools path.</value>
         string MediaToolsPath { get; }
+
+        /// <summary>
+        /// Gets the downloaded images data path.
+        /// </summary>
+        /// <value>The downloaded images data path.</value>
+        string DownloadedImagesDataPath { get; }
     }
 }

+ 21 - 153
MediaBrowser.Controller/Kernel.cs

@@ -1,22 +1,13 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.MediaInfo;
 using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Weather;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Threading;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller
@@ -24,7 +15,7 @@ namespace MediaBrowser.Controller
     /// <summary>
     /// Class Kernel
     /// </summary>
-    public class Kernel : IDisposable
+    public class Kernel 
     {
         /// <summary>
         /// Gets the instance.
@@ -36,25 +27,13 @@ namespace MediaBrowser.Controller
         /// Gets the image manager.
         /// </summary>
         /// <value>The image manager.</value>
-        public ImageManager ImageManager { get; private set; }
+        public ImageManager ImageManager { get; set; }
 
         /// <summary>
         /// Gets the FFMPEG controller.
         /// </summary>
         /// <value>The FFMPEG controller.</value>
-        public FFMpegManager FFMpegManager { get; private set; }
-
-        /// <summary>
-        /// Gets or sets the file system manager.
-        /// </summary>
-        /// <value>The file system manager.</value>
-        public FileSystemManager FileSystemManager { get; private set; }
-
-        /// <summary>
-        /// Gets the provider manager.
-        /// </summary>
-        /// <value>The provider manager.</value>
-        public ProviderManager ProviderManager { get; private set; }
+        public FFMpegManager FFMpegManager { get; set; }
 
         /// <summary>
         /// Gets the name of the web application that can be used for url building.
@@ -82,74 +61,68 @@ namespace MediaBrowser.Controller
         /// Gets the list of Localized string files
         /// </summary>
         /// <value>The string files.</value>
-        public IEnumerable<LocalizedStringData> StringFiles { get; private set; }
+        public IEnumerable<LocalizedStringData> StringFiles { get; set; }
 
         /// <summary>
         /// Gets the list of currently registered weather prvoiders
         /// </summary>
         /// <value>The weather providers.</value>
-        public IEnumerable<IWeatherProvider> WeatherProviders { get; private set; }
-
-        /// <summary>
-        /// Gets the list of currently registered metadata prvoiders
-        /// </summary>
-        /// <value>The metadata providers enumerable.</value>
-        public BaseMetadataProvider[] MetadataProviders { get; private set; }
+        public IEnumerable<IWeatherProvider> WeatherProviders { get; set; }
 
         /// <summary>
         /// Gets the list of currently registered image processors
         /// Image processors are specialized metadata providers that run after the normal ones
         /// </summary>
         /// <value>The image enhancers.</value>
-        public IEnumerable<IImageEnhancer> ImageEnhancers { get; private set; }
+        public IEnumerable<IImageEnhancer> ImageEnhancers { get; set; }
 
         /// <summary>
         /// Gets the list of available user repositories
         /// </summary>
         /// <value>The user repositories.</value>
-        private IEnumerable<IUserRepository> UserRepositories { get; set; }
+        public IEnumerable<IUserRepository> UserRepositories { get; set; }
 
         /// <summary>
         /// Gets the active user repository
         /// </summary>
         /// <value>The user repository.</value>
-        public IUserRepository UserRepository { get; private set; }
+        public IUserRepository UserRepository { get; set; }
 
         /// <summary>
         /// Gets the active user repository
         /// </summary>
         /// <value>The display preferences repository.</value>
-        public IDisplayPreferencesRepository DisplayPreferencesRepository { get; private set; }
+        public IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; }
 
         /// <summary>
         /// Gets the list of available item repositories
         /// </summary>
         /// <value>The item repositories.</value>
-        private IEnumerable<IItemRepository> ItemRepositories { get; set; }
+        public IEnumerable<IItemRepository> ItemRepositories { get; set; }
 
         /// <summary>
         /// Gets the active item repository
         /// </summary>
         /// <value>The item repository.</value>
-        public IItemRepository ItemRepository { get; private set; }
+        public IItemRepository ItemRepository { get; set; }
 
         /// <summary>
         /// Gets the list of available DisplayPreferencesRepositories
         /// </summary>
         /// <value>The display preferences repositories.</value>
-        private IEnumerable<IDisplayPreferencesRepository> DisplayPreferencesRepositories { get; set; }
+        public IEnumerable<IDisplayPreferencesRepository> DisplayPreferencesRepositories { get; set; }
 
         /// <summary>
         /// Gets the list of available item repositories
         /// </summary>
         /// <value>The user data repositories.</value>
-        private IEnumerable<IUserDataRepository> UserDataRepositories { get; set; }
+        public IEnumerable<IUserDataRepository> UserDataRepositories { get; set; }
 
         /// <summary>
         /// Gets the active user data repository
         /// </summary>
         /// <value>The user data repository.</value>
-        public IUserDataRepository UserDataRepository { get; private set; }
+        public IUserDataRepository UserDataRepository { get; set; }
 
         /// <summary>
         /// Gets the UDP server port number.
@@ -160,121 +133,39 @@ namespace MediaBrowser.Controller
             get { return 7359; }
         }
 
-        private readonly IXmlSerializer _xmlSerializer;
-
         private readonly IServerConfigurationManager _configurationManager;
-        private readonly ILogManager _logManager;
-        private IApplicationHost ApplicationHost { get; set; }
 
         /// <summary>
         /// Creates a kernel based on a Data path, which is akin to our current programdata path
         /// </summary>
-        /// <param name="appHost">The app host.</param>
-        /// <param name="xmlSerializer">The XML serializer.</param>
-        /// <param name="logManager">The log manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
-        /// <exception cref="System.ArgumentNullException">isoManager</exception>
-        public Kernel(IApplicationHost appHost, IXmlSerializer xmlSerializer, ILogManager logManager, IServerConfigurationManager configurationManager)
+        public Kernel(IServerConfigurationManager configurationManager)
         {
             Instance = this;
 
-            ApplicationHost = appHost;
             _configurationManager = configurationManager;
-            _xmlSerializer = xmlSerializer;
-            _logManager = logManager;
-            
-            // For now there's no real way to inject these properly
-            BaseItem.Logger = logManager.GetLogger("BaseItem");
-            User.XmlSerializer = _xmlSerializer;
-            Ratings.ConfigurationManager = _configurationManager;
-            LocalizedStrings.ApplicationPaths = _configurationManager.ApplicationPaths;
-            BaseItem.ConfigurationManager = configurationManager;
-        }
-
-        /// <summary>
-        /// Composes the parts with ioc container.
-        /// </summary>
-        protected void FindParts()
-        {
-            // For now there's no real way to inject these properly
-            BaseItem.LibraryManager = ApplicationHost.Resolve<ILibraryManager>();
-            User.UserManager = ApplicationHost.Resolve<IUserManager>();
-
-            FFMpegManager = (FFMpegManager)ApplicationHost.CreateInstance(typeof(FFMpegManager));
-            ImageManager = (ImageManager)ApplicationHost.CreateInstance(typeof(ImageManager));
-            ProviderManager = (ProviderManager)ApplicationHost.CreateInstance(typeof(ProviderManager));
-            
-            UserDataRepositories = ApplicationHost.GetExports<IUserDataRepository>();
-            UserRepositories = ApplicationHost.GetExports<IUserRepository>();
-            DisplayPreferencesRepositories = ApplicationHost.GetExports<IDisplayPreferencesRepository>();
-            ItemRepositories = ApplicationHost.GetExports<IItemRepository>();
-            WeatherProviders = ApplicationHost.GetExports<IWeatherProvider>();
-            ImageEnhancers = ApplicationHost.GetExports<IImageEnhancer>().OrderBy(e => e.Priority).ToArray();
-            StringFiles = ApplicationHost.GetExports<LocalizedStringData>();
-            MetadataProviders = ApplicationHost.GetExports<BaseMetadataProvider>().OrderBy(e => e.Priority).ToArray();
-        }
-
-        /// <summary>
-        /// Performs initializations that can be reloaded at anytime
-        /// </summary>
-        /// <returns>Task.</returns>
-        public async Task Init()
-        {
-            FindParts();
-
-            await LoadRepositories().ConfigureAwait(false);
-
-            await ApplicationHost.Resolve<IUserManager>().RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false);
-
-            foreach (var entryPoint in ApplicationHost.GetExports<IServerEntryPoint>())
-            {
-                entryPoint.Run();
-            }
-
-            ReloadFileSystemManager();
-        }
-
-        /// <summary>
-        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
-        /// </summary>
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Releases unmanaged and - optionally - managed resources.
-        /// </summary>
-        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool dispose)
-        {
-            if (dispose)
-            {
-                DisposeFileSystemManager();
-            }
         }
 
         /// <summary>
         /// Called when [composable parts loaded].
         /// </summary>
         /// <returns>Task.</returns>
-        protected Task LoadRepositories()
+        public Task LoadRepositories(IServerConfigurationManager configurationManager)
         {
             // Get the current item repository
-            ItemRepository = GetRepository(ItemRepositories, _configurationManager.Configuration.ItemRepository);
+            ItemRepository = GetRepository(ItemRepositories, configurationManager.Configuration.ItemRepository);
             var itemRepoTask = ItemRepository.Initialize();
 
             // Get the current user repository
-            UserRepository = GetRepository(UserRepositories, _configurationManager.Configuration.UserRepository);
+            UserRepository = GetRepository(UserRepositories, configurationManager.Configuration.UserRepository);
             var userRepoTask = UserRepository.Initialize();
 
             // Get the current item repository
-            UserDataRepository = GetRepository(UserDataRepositories, _configurationManager.Configuration.UserDataRepository);
+            UserDataRepository = GetRepository(UserDataRepositories, configurationManager.Configuration.UserDataRepository);
             var userDataRepoTask = UserDataRepository.Initialize();
 
             // Get the current display preferences repository
-            DisplayPreferencesRepository = GetRepository(DisplayPreferencesRepositories, _configurationManager.Configuration.DisplayPreferencesRepository);
+            DisplayPreferencesRepository = GetRepository(DisplayPreferencesRepositories, configurationManager.Configuration.DisplayPreferencesRepository);
             var displayPreferencesRepoTask = DisplayPreferencesRepository.Initialize();
 
             return Task.WhenAll(itemRepoTask, userRepoTask, userDataRepoTask, displayPreferencesRepoTask);
@@ -295,28 +186,5 @@ namespace MediaBrowser.Controller
             return enumerable.FirstOrDefault(r => string.Equals(r.Name, name, StringComparison.OrdinalIgnoreCase)) ??
                    enumerable.FirstOrDefault();
         }
-
-        /// <summary>
-        /// Disposes the file system manager.
-        /// </summary>
-        private void DisposeFileSystemManager()
-        {
-            if (FileSystemManager != null)
-            {
-                FileSystemManager.Dispose();
-                FileSystemManager = null;
-            }
-        }
-
-        /// <summary>
-        /// Reloads the file system manager.
-        /// </summary>
-        private void ReloadFileSystemManager()
-        {
-            DisposeFileSystemManager();
-
-            FileSystemManager = new FileSystemManager(_logManager, ApplicationHost.Resolve<ITaskManager>(), ApplicationHost.Resolve<ILibraryManager>(), _configurationManager);
-            FileSystemManager.StartWatchers();
-        }
     }
 }

+ 1 - 1
MediaBrowser.Controller/Localization/LocalizedStrings.cs

@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Localization
     /// </summary>
     public class LocalizedStrings
     {
-        internal static IServerApplicationPaths ApplicationPaths;
+        public static IServerApplicationPaths ApplicationPaths;
         
         /// <summary>
         /// The base prefix

+ 1 - 1
MediaBrowser.Controller/Localization/Ratings.cs

@@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Localization
     /// </summary>
     public static class Ratings
     {
-        internal static IServerConfigurationManager ConfigurationManager;
+        public static IServerConfigurationManager ConfigurationManager;
 
         /// <summary>
         /// The ratings def

+ 3 - 3
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -102,12 +102,14 @@
     <Compile Include="Entities\Year.cs" />
     <Compile Include="Extensions\XmlExtensions.cs" />
     <Compile Include="IO\FileSystem.cs" />
-    <Compile Include="IO\FileSystemManager.cs" />
+    <Compile Include="IO\IDirectoryWatchers.cs" />
     <Compile Include="IO\NativeMethods.cs" />
     <Compile Include="IServerApplicationHost.cs" />
     <Compile Include="IServerApplicationPaths.cs" />
     <Compile Include="Library\ChildrenChangedEventArgs.cs" />
     <Compile Include="Library\DtoBuilder.cs" />
+    <Compile Include="Providers\IProviderManager.cs" />
+    <Compile Include="Providers\MetadataProviderPriority.cs" />
     <Compile Include="Providers\Music\LastfmAlbumProvider.cs" />
     <Compile Include="Providers\Music\FanArtAlbumProvider.cs" />
     <Compile Include="Providers\Music\FanArtArtistProvider.cs" />
@@ -154,7 +156,6 @@
     <Compile Include="Providers\Movies\MovieProviderFromXml.cs" />
     <Compile Include="Providers\Movies\PersonProviderFromJson.cs" />
     <Compile Include="Providers\Movies\TmdbPersonProvider.cs" />
-    <Compile Include="Providers\ProviderManager.cs" />
     <Compile Include="Providers\SortNameProvider.cs" />
     <Compile Include="Providers\TV\EpisodeImageFromMediaLocationProvider.cs" />
     <Compile Include="Providers\TV\EpisodeProviderFromXml.cs" />
@@ -176,7 +177,6 @@
     <Compile Include="Library\TVUtils.cs" />
     <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" />
     <Compile Include="Library\ItemResolveArgs.cs" />
-    <Compile Include="IO\DirectoryWatchers.cs" />
     <Compile Include="IO\FileData.cs" />
     <Compile Include="Kernel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 5 - 132
MediaBrowser.Controller/Providers/BaseMetadataProvider.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Logging;
@@ -12,7 +11,7 @@ namespace MediaBrowser.Controller.Providers
     /// <summary>
     /// Class BaseMetadataProvider
     /// </summary>
-    public abstract class BaseMetadataProvider : IDisposable
+    public abstract class BaseMetadataProvider
     {
         /// <summary>
         /// Gets the logger.
@@ -27,16 +26,6 @@ namespace MediaBrowser.Controller.Providers
         /// <value>The configuration manager.</value>
         protected IServerConfigurationManager ConfigurationManager { get; private set; }
 
-        // Cache these since they will be used a lot
-        /// <summary>
-        /// The false task result
-        /// </summary>
-        protected static readonly Task<bool> FalseTaskResult = Task.FromResult(false);
-        /// <summary>
-        /// The true task result
-        /// </summary>
-        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
-
         /// <summary>
         /// The _id
         /// </summary>
@@ -135,7 +124,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="providerVersion">The provider version.</param>
         /// <param name="status">The status.</param>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        protected virtual void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
+        public virtual void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
         {
             if (item == null)
             {
@@ -162,7 +151,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="item">The item.</param>
         /// <param name="value">The value.</param>
         /// <param name="status">The status.</param>
-        protected virtual void SetLastRefreshed(BaseItem item, DateTime value, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
+        public void SetLastRefreshed(BaseItem item, DateTime value, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
         {
             SetLastRefreshed(item, value, ProviderVersion, status);
         }
@@ -254,76 +243,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
         /// <exception cref="System.ArgumentNullException"></exception>
-        public async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
-        {
-            if (item == null)
-            {
-                throw new ArgumentNullException();
-            }
-
-            cancellationToken.ThrowIfCancellationRequested();
-            
-            Logger.Info("Running for {0}", item.Path ?? item.Name ?? "--Unknown--");
-
-            // This provides the ability to cancel just this one provider
-            var innerCancellationTokenSource = new CancellationTokenSource();
-
-            Kernel.Instance.ProviderManager.OnProviderRefreshBeginning(this, item, innerCancellationTokenSource);
-
-            try
-            {
-                var task = FetchAsyncInternal(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token);
-
-                await task.ConfigureAwait(false);
-
-                if (task.IsFaulted)
-                {
-                    // Log the AggregateException
-                    if (task.Exception != null)
-                    {
-                        Logger.ErrorException("AggregateException:", task.Exception);
-                    }
-
-                    return false;
-                }
-
-                return task.Result;
-            }
-            catch (OperationCanceledException ex)
-            {
-                Logger.Info("{0} cancelled for {1}", GetType().Name, item.Name);
-
-                // If the outer cancellation token is the one that caused the cancellation, throw it
-                if (cancellationToken.IsCancellationRequested && ex.CancellationToken == cancellationToken)
-                {
-                    throw;
-                }
-
-                return false;
-            }
-            catch (Exception ex)
-            {
-                Logger.ErrorException("failed refreshing {0}", ex, item.Name);
-
-                SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.Failure);
-                return true;
-            }
-            finally
-            {
-                innerCancellationTokenSource.Dispose();
-                
-                Kernel.Instance.ProviderManager.OnProviderRefreshCompleted(this, item);
-            }
-        }
-
-        /// <summary>
-        /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="force">if set to <c>true</c> [force].</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{System.Boolean}.</returns>
-        protected abstract Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken);
+        public abstract Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the priority.
@@ -331,23 +251,6 @@ namespace MediaBrowser.Controller.Providers
         /// <value>The priority.</value>
         public abstract MetadataProviderPriority Priority { get; }
 
-        /// <summary>
-        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
-        /// </summary>
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Releases unmanaged and - optionally - managed resources.
-        /// </summary>
-        /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool dispose)
-        {
-        }
-
         /// <summary>
         /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes
         /// </summary>
@@ -386,34 +289,4 @@ namespace MediaBrowser.Controller.Providers
             return item.FileSystemStamp;
         }
     }
-
-    /// <summary>
-    /// Determines when a provider should execute, relative to others
-    /// </summary>
-    public enum MetadataProviderPriority
-    {
-        // Run this provider at the beginning
-        /// <summary>
-        /// The first
-        /// </summary>
-        First = 1,
-
-        // Run this provider after all first priority providers
-        /// <summary>
-        /// The second
-        /// </summary>
-        Second = 2,
-
-        // Run this provider after all second priority providers
-        /// <summary>
-        /// The third
-        /// </summary>
-        Third = 3,
-
-        // Run this provider last
-        /// <summary>
-        /// The last
-        /// </summary>
-        Last = 4
-    }
 }

+ 1 - 1
MediaBrowser.Controller/Providers/FolderProviderFromXml.cs

@@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return Task.Run(() => Fetch(item, cancellationToken));
         }

+ 51 - 0
MediaBrowser.Controller/Providers/IProviderManager.cs

@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Providers
+{
+    public interface IProviderManager : IDisposable
+    {
+        /// <summary>
+        /// Downloads the and save image.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="source">The source.</param>
+        /// <param name="targetName">Name of the target.</param>
+        /// <param name="resourcePool">The resource pool.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{System.String}.</returns>
+        /// <exception cref="System.ArgumentNullException">item</exception>
+        Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, SemaphoreSlim resourcePool, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Saves to library filesystem.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="path">The path.</param>
+        /// <param name="dataToSave">The data to save.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException"></exception>
+        Task SaveToLibraryFilesystem(BaseItem item, string path, Stream dataToSave, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Executes the metadata providers.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <param name="force">if set to <c>true</c> [force].</param>
+        /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
+        /// <returns>Task{System.Boolean}.</returns>
+        Task<bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true);
+
+        /// <summary>
+        /// Adds the metadata providers.
+        /// </summary>
+        /// <param name="providers">The providers.</param>
+        void AddMetadataProviders(IEnumerable<BaseMetadataProvider> providers);
+    }
+}

+ 6 - 1
MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs

@@ -52,6 +52,11 @@ namespace MediaBrowser.Controller.Providers
             }
         }
 
+        /// <summary>
+        /// The true task result
+        /// </summary>
+        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
+
         /// <summary>
         /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
         /// </summary>
@@ -59,7 +64,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             

+ 7 - 4
MediaBrowser.Controller/Providers/MediaInfo/BaseFFProbeProvider.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
     /// Provides a base class for extracting media information through ffprobe
     /// </summary>
     /// <typeparam name="T"></typeparam>
-    public abstract class BaseFFProbeProvider<T> : BaseFFMpegProvider<T>
+    public abstract class BaseFFProbeProvider<T> : BaseFFMpegProvider<T>, IDisposable
         where T : BaseItem
     {
         protected BaseFFProbeProvider(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager)
@@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             var myItem = (T)item;
 
@@ -351,14 +351,17 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
         /// Releases unmanaged and - optionally - managed resources.
         /// </summary>
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected override void Dispose(bool dispose)
+        protected virtual void Dispose(bool dispose)
         {
             if (dispose)
             {
                 FFProbeCache.Dispose();
             }
+        }
 
-            base.Dispose(dispose);
+        public void Dispose()
+        {
+            Dispose(true);
         }
     }
 }

+ 6 - 1
MediaBrowser.Controller/Providers/MediaInfo/FFMpegAudioImageProvider.cs

@@ -19,6 +19,11 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
         {
         }
 
+        /// <summary>
+        /// The true task result
+        /// </summary>
+        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
+
         /// <summary>
         /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
         /// </summary>
@@ -26,7 +31,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             var audio = (Audio)item;
 

+ 6 - 1
MediaBrowser.Controller/Providers/MediaInfo/FFMpegVideoImageProvider.cs

@@ -59,6 +59,11 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
             return false;
         }
 
+        /// <summary>
+        /// The true task result
+        /// </summary>
+        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
+
         /// <summary>
         /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
         /// </summary>
@@ -66,7 +71,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(item.PrimaryImagePath))
             {

+ 33 - 0
MediaBrowser.Controller/Providers/MetadataProviderPriority.cs

@@ -0,0 +1,33 @@
+
+namespace MediaBrowser.Controller.Providers
+{
+    /// <summary>
+    /// Determines when a provider should execute, relative to others
+    /// </summary>
+    public enum MetadataProviderPriority
+    {
+        // Run this provider at the beginning
+        /// <summary>
+        /// The first
+        /// </summary>
+        First = 1,
+
+        // Run this provider after all first priority providers
+        /// <summary>
+        /// The second
+        /// </summary>
+        Second = 2,
+
+        // Run this provider after all second priority providers
+        /// <summary>
+        /// The third
+        /// </summary>
+        Third = 3,
+
+        // Run this provider last
+        /// <summary>
+        /// The last
+        /// </summary>
+        Last = 4
+    }
+}

+ 17 - 10
MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Providers.Movies
     /// <summary>
     /// Class FanArtMovieProvider
     /// </summary>
-    class FanArtMovieProvider : FanartBaseProvider
+    class FanArtMovieProvider : FanartBaseProvider, IDisposable
     {
         /// <summary>
         /// The fan art
@@ -32,6 +32,8 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <value>The HTTP client.</value>
         protected IHttpClient HttpClient { get; private set; }
 
+        private readonly IProviderManager _providerManager;
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="FanArtMovieProvider" /> class.
         /// </summary>
@@ -39,7 +41,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="logManager">The log manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <exception cref="System.ArgumentNullException">httpClient</exception>
-        public FanArtMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        public FanArtMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             if (httpClient == null)
@@ -47,19 +49,19 @@ namespace MediaBrowser.Controller.Providers.Movies
                 throw new ArgumentNullException("httpClient");
             }
             HttpClient = httpClient;
+            _providerManager = providerManager;
         }
 
         /// <summary>
         /// Releases unmanaged and - optionally - managed resources.
         /// </summary>
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected override void Dispose(bool dispose)
+        protected virtual void Dispose(bool dispose)
         {
             if (dispose)
             {
                 FanArtResourcePool.Dispose();
             }
-            base.Dispose(dispose);
         }
         
         /// <summary>
@@ -103,7 +105,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -147,7 +149,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                             Logger.Debug("FanArtProvider getting ClearLogo for " + movie.Name);
                             try
                             {
-                                movie.SetImage(ImageType.Logo, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(movie, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                movie.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(movie, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -173,7 +175,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                             Logger.Debug("FanArtProvider getting ClearArt for " + movie.Name);
                             try
                             {
-                                movie.SetImage(ImageType.Art, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(movie, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                movie.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(movie, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -196,7 +198,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                             Logger.Debug("FanArtProvider getting DiscArt for " + movie.Name);
                             try
                             {
-                                movie.SetImage(ImageType.Disc, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(movie, path, DISC_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                movie.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(movie, path, DISC_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -220,7 +222,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                             Logger.Debug("FanArtProvider getting Banner for " + movie.Name);
                             try
                             {
-                                movie.SetImage(ImageType.Banner, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(movie, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                movie.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(movie, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -244,7 +246,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                             Logger.Debug("FanArtProvider getting Banner for " + movie.Name);
                             try
                             {
-                                movie.SetImage(ImageType.Thumb, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(movie, path, THUMB_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                movie.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(movie, path, THUMB_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -260,5 +262,10 @@ namespace MediaBrowser.Controller.Providers.Movies
             SetLastRefreshed(movie, DateTime.UtcNow);
             return true;
         }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
     }
 }

+ 19 - 11
MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs

@@ -30,8 +30,10 @@ namespace MediaBrowser.Controller.Providers.Movies
     /// <summary>
     /// Class MovieDbProvider
     /// </summary>
-    public class MovieDbProvider : BaseMetadataProvider
+    public class MovieDbProvider : BaseMetadataProvider, IDisposable
     {
+        protected readonly IProviderManager ProviderManager;
+        
         /// <summary>
         /// The movie db
         /// </summary>
@@ -58,11 +60,12 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="jsonSerializer">The json serializer.</param>
         /// <param name="httpClient">The HTTP client.</param>
-        public MovieDbProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient)
+        public MovieDbProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             JsonSerializer = jsonSerializer;
             HttpClient = httpClient;
+            ProviderManager = providerManager;
             Current = this;
         }
 
@@ -70,13 +73,12 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// Releases unmanaged and - optionally - managed resources.
         /// </summary>
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected override void Dispose(bool dispose)
+        protected virtual void Dispose(bool dispose)
         {
             if (dispose)
             {
                 MovieDbResourcePool.Dispose();
             }
-            base.Dispose(dispose);
         }
 
         /// <summary>
@@ -209,16 +211,17 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="value">The value.</param>
+        /// <param name="providerVersion">The provider version.</param>
         /// <param name="status">The status.</param>
-        protected override void SetLastRefreshed(BaseItem item, DateTime value, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
+        public override void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
         {
-            base.SetLastRefreshed(item, value, status);
+            base.SetLastRefreshed(item, value, providerVersion, status);
 
             if (ConfigurationManager.Configuration.SaveLocalMeta)
             {
                 //in addition to ours, we need to set the last refreshed time for the local data provider
                 //so it won't see the new files we download and process them all over again
-                if (JsonProvider == null) JsonProvider = new MovieProviderFromJson(LogManager, ConfigurationManager, JsonSerializer, HttpClient);
+                if (JsonProvider == null) JsonProvider = new MovieProviderFromJson(LogManager, ConfigurationManager, JsonSerializer, HttpClient, ProviderManager);
                 var data = item.ProviderData.GetValueOrDefault(JsonProvider.Id, new BaseProviderInfo { ProviderId = JsonProvider.Id });
                 data.LastRefreshed = value;
                 item.ProviderData[JsonProvider.Id] = data;
@@ -291,7 +294,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             if (HasAltMeta(item))
             {
@@ -724,7 +727,7 @@ namespace MediaBrowser.Controller.Providers.Movies
 
                 cancellationToken.ThrowIfCancellationRequested();
 
-                await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LOCAL_META_FILE_NAME), ms, cancellationToken).ConfigureAwait(false);
+                await ProviderManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LOCAL_META_FILE_NAME), ms, cancellationToken).ConfigureAwait(false);
             }
         }
 
@@ -1018,7 +1021,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                 {
                     try
                     {
-                        item.PrimaryImagePath = await Kernel.Instance.ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + poster.file_path, "folder" + Path.GetExtension(poster.file_path), MovieDbResourcePool, cancellationToken).ConfigureAwait(false);
+                        item.PrimaryImagePath = await ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + poster.file_path, "folder" + Path.GetExtension(poster.file_path), MovieDbResourcePool, cancellationToken).ConfigureAwait(false);
                     }
                     catch (HttpException)
                     {
@@ -1050,7 +1053,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                     {
                         try
                         {
-                            item.BackdropImagePaths.Add(await Kernel.Instance.ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + images.backdrops[i].file_path, bdName + Path.GetExtension(images.backdrops[i].file_path), MovieDbResourcePool, cancellationToken).ConfigureAwait(false));
+                            item.BackdropImagePaths.Add(await ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + images.backdrops[i].file_path, bdName + Path.GetExtension(images.backdrops[i].file_path), MovieDbResourcePool, cancellationToken).ConfigureAwait(false));
                         }
                         catch (HttpException)
                         {
@@ -1661,5 +1664,10 @@ namespace MediaBrowser.Controller.Providers.Movies
             public TmdbImageSettings images { get; set; }
         }
         #endregion
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
     }
 }

+ 3 - 3
MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs

@@ -15,8 +15,8 @@ namespace MediaBrowser.Controller.Providers.Movies
     /// </summary>
     public class MovieProviderFromJson : MovieDbProvider
     {
-        public MovieProviderFromJson(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient) : 
-            base(logManager, configurationManager, jsonSerializer, httpClient)
+        public MovieProviderFromJson(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager)
+            : base(logManager, configurationManager, jsonSerializer, httpClient, providerManager)
         {
         }
 
@@ -79,7 +79,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             // Since we don't have anything truly async, and since deserializing can be expensive, create a task to force parallelism
             return Task.Run(() =>

+ 1 - 1
MediaBrowser.Controller/Providers/Movies/MovieProviderFromXml.cs

@@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return Task.Run(() => Fetch(item, cancellationToken));
         }

+ 3 - 3
MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs

@@ -15,8 +15,8 @@ namespace MediaBrowser.Controller.Providers.Movies
     /// </summary>
     class PersonProviderFromJson : TmdbPersonProvider
     {
-        public PersonProviderFromJson(IHttpClient httpClient, IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager) : 
-            base(httpClient, jsonSerializer, logManager, configurationManager)
+        public PersonProviderFromJson(IHttpClient httpClient, IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) 
+            : base(httpClient, jsonSerializer, logManager, configurationManager, providerManager)
         {
         }
 
@@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return Task.Run(() =>
             {

+ 8 - 5
MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs

@@ -26,7 +26,9 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// </summary>
         protected const string MetaFileName = "MBPerson.json";
 
-        public TmdbPersonProvider(IHttpClient httpClient, IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager)
+        protected readonly IProviderManager ProviderManager;
+        
+        public TmdbPersonProvider(IHttpClient httpClient, IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             if (jsonSerializer == null)
@@ -39,6 +41,7 @@ namespace MediaBrowser.Controller.Providers.Movies
             }
             HttpClient = httpClient;
             JsonSerializer = jsonSerializer;
+            ProviderManager = providerManager;
         }
 
         /// <summary>
@@ -83,7 +86,7 @@ namespace MediaBrowser.Controller.Providers.Movies
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -209,7 +212,7 @@ namespace MediaBrowser.Controller.Providers.Movies
 
                 JsonSerializer.SerializeToStream(searchResult, memoryStream);
 
-                await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(person, Path.Combine(person.MetaLocation, MetaFileName), memoryStream, cancellationToken);
+                await ProviderManager.SaveToLibraryFilesystem(person, Path.Combine(person.MetaLocation, MetaFileName), memoryStream, cancellationToken);
 
                 Logger.Debug("TmdbPersonProvider downloaded and saved information for {0}", person.Name);
             }
@@ -288,7 +291,7 @@ namespace MediaBrowser.Controller.Providers.Movies
                 }
                 if (profile != null)
                 {
-                    var tmdbSettings = await Kernel.Instance.MetadataProviders.OfType<MovieDbProvider>().First().TmdbSettings.ConfigureAwait(false);
+                    var tmdbSettings = await MovieDbProvider.Current.TmdbSettings.ConfigureAwait(false);
 
                     var img = await DownloadAndSaveImage(person, tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedProfileSize + profile.File_Path,
                                              "folder" + Path.GetExtension(profile.File_Path), cancellationToken).ConfigureAwait(false);
@@ -319,7 +322,7 @@ namespace MediaBrowser.Controller.Providers.Movies
             {
                 using (var sourceStream = await HttpClient.GetMemoryStream(source, MovieDbProvider.Current.MovieDbResourcePool, cancellationToken).ConfigureAwait(false))
                 {
-                    await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(item, localPath, sourceStream, cancellationToken).ConfigureAwait(false);
+                    await ProviderManager.SaveToLibraryFilesystem(item, localPath, sourceStream, cancellationToken).ConfigureAwait(false);
 
                     Logger.Debug("TmdbPersonProvider downloaded and saved image for {0}", item.Name);
                 }

+ 7 - 3
MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs

@@ -15,8 +15,12 @@ namespace MediaBrowser.Controller.Providers.Music
 {
     public class FanArtAlbumProvider : FanartBaseProvider
     {
-        public FanArtAlbumProvider(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager)
+        private readonly IProviderManager _providerManager;
+        
+        public FanArtAlbumProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
+            : base(logManager, configurationManager)
         {
+            _providerManager = providerManager;
         }
 
         public override bool Supports(BaseItem item)
@@ -37,7 +41,7 @@ namespace MediaBrowser.Controller.Providers.Music
                    DateTime.Today.Subtract(providerInfo.LastRefreshed).TotalDays > ConfigurationManager.Configuration.MetadataRefreshDays;
         }
 
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             var mbid = item.GetProviderId(MetadataProviders.Musicbrainz);
             if (mbid == null)
@@ -67,7 +71,7 @@ namespace MediaBrowser.Controller.Providers.Music
                 return false;
             }
 
-            item.SetImage(ImageType.Primary, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(item, cover, "folder.jpg", FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+            item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, cover, "folder.jpg", FanArtResourcePool, cancellationToken).ConfigureAwait(false));
             return true;
         }
     }

+ 10 - 7
MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs

@@ -28,7 +28,9 @@ namespace MediaBrowser.Controller.Providers.Music
         /// <value>The HTTP client.</value>
         protected IHttpClient HttpClient { get; private set; }
 
-        public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        private readonly IProviderManager _providerManager;
+
+        public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             if (httpClient == null)
@@ -36,6 +38,7 @@ namespace MediaBrowser.Controller.Providers.Music
                 throw new ArgumentNullException("httpClient");
             }
             HttpClient = httpClient;
+            _providerManager = providerManager;
         }
 
         /// <summary>
@@ -80,7 +83,7 @@ namespace MediaBrowser.Controller.Providers.Music
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -118,7 +121,7 @@ namespace MediaBrowser.Controller.Providers.Music
                             Logger.Debug("FanArtProvider getting ClearLogo for " + artist.Name);
                             try
                             {
-                                artist.SetImage(ImageType.Logo, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                artist.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(artist, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -146,7 +149,7 @@ namespace MediaBrowser.Controller.Providers.Music
                                     Logger.Debug("FanArtProvider getting Backdrop for " + artist.Name);
                                     try
                                     {
-                                        artist.BackdropImagePaths.Add(await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ("Backdrop"+(numBackdrops > 0 ? numBackdrops.ToString() : "")+".jpg"), FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                        artist.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(artist, path, ("Backdrop" + (numBackdrops > 0 ? numBackdrops.ToString() : "") + ".jpg"), FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                                         numBackdrops++;
                                         if (numBackdrops >= ConfigurationManager.Configuration.MaxBackdrops) break;
                                     }
@@ -203,7 +206,7 @@ namespace MediaBrowser.Controller.Providers.Music
                             Logger.Debug("FanArtProvider getting ClearArt for " + artist.Name);
                             try
                             {
-                                artist.SetImage(ImageType.Art, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                artist.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(artist, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -226,7 +229,7 @@ namespace MediaBrowser.Controller.Providers.Music
                             Logger.Debug("FanArtProvider getting Banner for " + artist.Name);
                             try
                             {
-                                artist.SetImage(ImageType.Banner, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                artist.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(artist, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -250,7 +253,7 @@ namespace MediaBrowser.Controller.Providers.Music
                             Logger.Debug("FanArtProvider getting Primary image for " + artist.Name);
                             try
                             {
-                                artist.SetImage(ImageType.Primary, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, PRIMARY_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                artist.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(artist, path, PRIMARY_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {

+ 5 - 2
MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs

@@ -16,9 +16,12 @@ namespace MediaBrowser.Controller.Providers.Music
     {
         private static readonly Task<string> BlankId = Task.FromResult("0000");
 
-        public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        private readonly IProviderManager _providerManager;
+        
+        public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(jsonSerializer, httpClient, logManager, configurationManager)
         {
+            _providerManager = providerManager;
             LocalMetaFileName = LastfmHelper.LocalAlbumMetaFileName;
         }
 
@@ -62,7 +65,7 @@ namespace MediaBrowser.Controller.Providers.Music
 
                     cancellationToken.ThrowIfCancellationRequested();
 
-                    await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false);
+                    await _providerManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false);
                     
                 }
             }

+ 5 - 3
MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs

@@ -19,10 +19,12 @@ namespace MediaBrowser.Controller.Providers.Music
 {
     public class LastfmArtistProvider : LastfmBaseProvider
     {
-
-        public LastfmArtistProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        private readonly IProviderManager _providerManager;
+        
+        public LastfmArtistProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(jsonSerializer, httpClient, logManager, configurationManager)
         {
+            _providerManager = providerManager;
             LocalMetaFileName = LastfmHelper.LocalArtistMetaFileName;
         }
 
@@ -91,7 +93,7 @@ namespace MediaBrowser.Controller.Providers.Music
 
                     cancellationToken.ThrowIfCancellationRequested();
 
-                    await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false);
+                    await _providerManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false);
                     
                 }
             }

+ 1 - 1
MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs

@@ -196,7 +196,7 @@ namespace MediaBrowser.Controller.Providers.Music
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             if (item.DontFetchMeta)
             {

+ 1 - 1
MediaBrowser.Controller/Providers/Music/MusicArtistProviderFromJson.cs

@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Providers.Music
 
         }
 
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return Task.Run(() =>
             {

+ 12 - 1
MediaBrowser.Controller/Providers/SortNameProvider.cs

@@ -49,6 +49,17 @@ namespace MediaBrowser.Controller.Providers
             return !string.IsNullOrEmpty(item.Name) && string.IsNullOrEmpty(item.SortName);
         }
 
+        // Cache these since they will be used a lot
+        /// <summary>
+        /// The false task result
+        /// </summary>
+        protected static readonly Task<bool> FalseTaskResult = Task.FromResult(false);
+
+        /// <summary>
+        /// The true task result
+        /// </summary>
+        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
+
         /// <summary>
         /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
         /// </summary>
@@ -56,7 +67,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return SetSortName(item, cancellationToken) ? TrueTaskResult : FalseTaskResult;
         }

+ 6 - 1
MediaBrowser.Controller/Providers/TV/EpisodeImageFromMediaLocationProvider.cs

@@ -50,6 +50,11 @@ namespace MediaBrowser.Controller.Providers.TV
             }
         }
 
+        /// <summary>
+        /// The true task result
+        /// </summary>
+        protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
+
         /// <summary>
         /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
         /// </summary>
@@ -57,7 +62,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             

+ 1 - 1
MediaBrowser.Controller/Providers/TV/EpisodeProviderFromXml.cs

@@ -53,7 +53,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return Task.Run(() => Fetch(item, cancellationToken));
         }

+ 8 - 5
MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs

@@ -25,7 +25,9 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <value>The HTTP client.</value>
         protected IHttpClient HttpClient { get; private set; }
 
-        public FanArtTvProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        private readonly IProviderManager _providerManager;
+        
+        public FanArtTvProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             if (httpClient == null)
@@ -33,6 +35,7 @@ namespace MediaBrowser.Controller.Providers.TV
                 throw new ArgumentNullException("httpClient");
             }
             HttpClient = httpClient;
+            _providerManager = providerManager;
         }
 
         public override bool Supports(BaseItem item)
@@ -53,7 +56,7 @@ namespace MediaBrowser.Controller.Providers.TV
                 || (!thumbExists && ConfigurationManager.Configuration.DownloadSeriesImages.Thumb);
         }
 
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -90,7 +93,7 @@ namespace MediaBrowser.Controller.Providers.TV
                             Logger.Debug("FanArtProvider getting ClearLogo for " + series.Name);
                             try
                             {
-                                series.SetImage(ImageType.Logo, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(series, path, LOGO_FILE, FanArtMovieProvider.Current.FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                series.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(series, path, LOGO_FILE, FanArtMovieProvider.Current.FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -114,7 +117,7 @@ namespace MediaBrowser.Controller.Providers.TV
                             Logger.Debug("FanArtProvider getting ClearArt for " + series.Name);
                             try
                             {
-                                series.SetImage(ImageType.Art, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(series, path, ART_FILE, FanArtMovieProvider.Current.FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                series.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(series, path, ART_FILE, FanArtMovieProvider.Current.FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {
@@ -138,7 +141,7 @@ namespace MediaBrowser.Controller.Providers.TV
                             Logger.Debug("FanArtProvider getting ThumbArt for " + series.Name);
                             try
                             {
-                                series.SetImage(ImageType.Disc, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(series, path, THUMB_FILE, FanArtMovieProvider.Current.FanArtResourcePool, cancellationToken).ConfigureAwait(false));
+                                series.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(series, path, THUMB_FILE, FanArtMovieProvider.Current.FanArtResourcePool, cancellationToken).ConfigureAwait(false));
                             }
                             catch (HttpException)
                             {

+ 7 - 4
MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs

@@ -22,6 +22,8 @@ namespace MediaBrowser.Controller.Providers.TV
     /// </summary>
     class RemoteEpisodeProvider : BaseMetadataProvider
     {
+        private readonly IProviderManager _providerManager;
+        
         /// <summary>
         /// Gets the HTTP client.
         /// </summary>
@@ -34,10 +36,11 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="httpClient">The HTTP client.</param>
         /// <param name="logManager">The log manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
-        public RemoteEpisodeProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        public RemoteEpisodeProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             HttpClient = httpClient;
+            _providerManager = providerManager;
         }
 
         /// <summary>
@@ -117,7 +120,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="item">The item.</param>
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             
@@ -229,7 +232,7 @@ namespace MediaBrowser.Controller.Providers.TV
 
                         try
                         {
-                            episode.PrimaryImagePath = await Kernel.Instance.ProviderManager.DownloadAndSaveImage(episode, TVUtils.BannerUrl + p, Path.GetFileName(p), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken);
+                            episode.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(episode, TVUtils.BannerUrl + p, Path.GetFileName(p), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken);
                         }
                         catch (HttpException)
                         {
@@ -282,7 +285,7 @@ namespace MediaBrowser.Controller.Providers.TV
                         var ms = new MemoryStream();
                         doc.Save(ms);
 
-                        await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(episode, Path.Combine(episode.MetaLocation, Path.GetFileNameWithoutExtension(episode.Path) + ".xml"), ms, cancellationToken).ConfigureAwait(false);
+                        await _providerManager.SaveToLibraryFilesystem(episode, Path.Combine(episode.MetaLocation, Path.GetFileNameWithoutExtension(episode.Path) + ".xml"), ms, cancellationToken).ConfigureAwait(false);
                     }
 
                     return true;

+ 9 - 8
MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs

@@ -25,8 +25,10 @@ namespace MediaBrowser.Controller.Providers.TV
         /// </summary>
         /// <value>The HTTP client.</value>
         protected IHttpClient HttpClient { get; private set; }
+
+        private readonly IProviderManager _providerManager;
         
-        public RemoteSeasonProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        public RemoteSeasonProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             if (httpClient == null)
@@ -34,6 +36,7 @@ namespace MediaBrowser.Controller.Providers.TV
                 throw new ArgumentNullException("httpClient");
             }
             HttpClient = httpClient;
+            _providerManager = providerManager;
         }
 
         /// <summary>
@@ -97,7 +100,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
@@ -174,7 +177,7 @@ namespace MediaBrowser.Controller.Providers.TV
                                 try
                                 {
                                     if (n != null)
-                                        season.PrimaryImagePath = await Kernel.Instance.ProviderManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false);
+                                        season.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false);
                                 }
                                 catch (HttpException)
                                 {
@@ -197,8 +200,7 @@ namespace MediaBrowser.Controller.Providers.TV
                                     try
                                     {
                                         var bannerImagePath =
-                                            await
-                                            Kernel.Instance.ProviderManager.DownloadAndSaveImage(season,
+                                            await _providerManager.DownloadAndSaveImage(season,
                                                                                              TVUtils.BannerUrl + n.InnerText,
                                                                                              "banner" +
                                                                                              Path.GetExtension(n.InnerText),
@@ -229,7 +231,7 @@ namespace MediaBrowser.Controller.Providers.TV
                                     try
                                     {
                                         if (season.BackdropImagePaths == null) season.BackdropImagePaths = new List<string>();
-                                        season.BackdropImagePaths.Add(await Kernel.Instance.ProviderManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false));
+                                        season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false));
                                     }
                                     catch (HttpException)
                                     {
@@ -257,8 +259,7 @@ namespace MediaBrowser.Controller.Providers.TV
                                         try
                                         {
                                             season.BackdropImagePaths.Add(
-                                                await
-                                                Kernel.Instance.ProviderManager.DownloadAndSaveImage(season,
+                                                await _providerManager.DownloadAndSaveImage(season,
                                                                                                  TVUtils.BannerUrl +
                                                                                                  n.InnerText,
                                                                                                  "backdrop" +

+ 16 - 12
MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs

@@ -22,8 +22,10 @@ namespace MediaBrowser.Controller.Providers.TV
     /// <summary>
     /// Class RemoteSeriesProvider
     /// </summary>
-    class RemoteSeriesProvider : BaseMetadataProvider
+    class RemoteSeriesProvider : BaseMetadataProvider, IDisposable
     {
+        private readonly IProviderManager _providerManager;
+        
         /// <summary>
         /// The tv db
         /// </summary>
@@ -44,7 +46,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="logManager">The log manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <exception cref="System.ArgumentNullException">httpClient</exception>
-        public RemoteSeriesProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager)
+        public RemoteSeriesProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
             : base(logManager, configurationManager)
         {
             if (httpClient == null)
@@ -52,6 +54,7 @@ namespace MediaBrowser.Controller.Providers.TV
                 throw new ArgumentNullException("httpClient");
             }
             HttpClient = httpClient;
+            _providerManager = providerManager;
             Current = this;
         }
 
@@ -59,13 +62,12 @@ namespace MediaBrowser.Controller.Providers.TV
         /// Releases unmanaged and - optionally - managed resources.
         /// </summary>
         /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected override void Dispose(bool dispose)
+        protected virtual void Dispose(bool dispose)
         {
             if (dispose)
             {
                 TvDbResourcePool.Dispose();
             }
-            base.Dispose(dispose);
         }
 
         /// <summary>
@@ -149,7 +151,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override async Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             
@@ -252,8 +254,8 @@ namespace MediaBrowser.Controller.Providers.TV
                     {
                         var ms = new MemoryStream();
                         doc.Save(ms);
-                        
-                        await Kernel.Instance.FileSystemManager.SaveToLibraryFilesystem(series, Path.Combine(series.MetaLocation, LOCAL_META_FILE_NAME), ms, cancellationToken).ConfigureAwait(false);
+
+                        await _providerManager.SaveToLibraryFilesystem(series, Path.Combine(series.MetaLocation, LOCAL_META_FILE_NAME), ms, cancellationToken).ConfigureAwait(false);
                     }
                 }
             }
@@ -366,7 +368,7 @@ namespace MediaBrowser.Controller.Providers.TV
                             {
                                 try
                                 {
-                                    series.PrimaryImagePath = await Kernel.Instance.ProviderManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), TvDbResourcePool, cancellationToken).ConfigureAwait(false);
+                                    series.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), TvDbResourcePool, cancellationToken).ConfigureAwait(false);
                                 }
                                 catch (HttpException)
                                 {
@@ -389,7 +391,7 @@ namespace MediaBrowser.Controller.Providers.TV
                             {
                                 try
                                 {
-                                    var bannerImagePath = await Kernel.Instance.ProviderManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), TvDbResourcePool, cancellationToken);
+                                    var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), TvDbResourcePool, cancellationToken);
 
                                     series.SetImage(ImageType.Banner, bannerImagePath);
                                 }
@@ -418,7 +420,7 @@ namespace MediaBrowser.Controller.Providers.TV
                                 {
                                     try
                                     {
-                                        series.BackdropImagePaths.Add(await Kernel.Instance.ProviderManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), TvDbResourcePool, cancellationToken).ConfigureAwait(false));
+                                        series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), TvDbResourcePool, cancellationToken).ConfigureAwait(false));
                                     }
                                     catch (HttpException)
                                     {
@@ -584,7 +586,9 @@ namespace MediaBrowser.Controller.Providers.TV
             return name.Trim();
         }
 
-
-
+        public void Dispose()
+        {
+            Dispose(true);
+        }
     }
 }

+ 1 - 1
MediaBrowser.Controller/Providers/TV/SeriesProviderFromXml.cs

@@ -57,7 +57,7 @@ namespace MediaBrowser.Controller.Providers.TV
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
-        protected override Task<bool> FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken)
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
             return Task.Run(() => Fetch(item, cancellationToken));
         }

+ 36 - 35
MediaBrowser.Controller/IO/DirectoryWatchers.cs → MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.ScheduledTasks;
 using MediaBrowser.Model.Logging;
@@ -12,35 +13,35 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace MediaBrowser.Controller.IO
+namespace MediaBrowser.Server.Implementations.IO
 {
     /// <summary>
     /// Class DirectoryWatchers
     /// </summary>
-    public class DirectoryWatchers : IDisposable
+    public class DirectoryWatchers : IDirectoryWatchers
     {
         /// <summary>
         /// The file system watchers
         /// </summary>
-        private ConcurrentBag<FileSystemWatcher> FileSystemWatchers = new ConcurrentBag<FileSystemWatcher>();
+        private ConcurrentBag<FileSystemWatcher> _fileSystemWatchers = new ConcurrentBag<FileSystemWatcher>();
         /// <summary>
         /// The update timer
         /// </summary>
-        private Timer updateTimer;
+        private Timer _updateTimer;
         /// <summary>
         /// The affected paths
         /// </summary>
-        private readonly ConcurrentDictionary<string, string> affectedPaths = new ConcurrentDictionary<string, string>();
+        private readonly ConcurrentDictionary<string, string> _affectedPaths = new ConcurrentDictionary<string, string>();
 
         /// <summary>
         /// A dynamic list of paths that should be ignored.  Added to during our own file sytem modifications.
         /// </summary>
-        private readonly ConcurrentDictionary<string,string> TempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        private readonly ConcurrentDictionary<string,string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
         /// <summary>
         /// The timer lock
         /// </summary>
-        private readonly object timerLock = new object();
+        private readonly object _timerLock = new object();
 
         /// <summary>
         /// Add the path to our temporary ignore list.  Use when writing to a path within our listening scope.
@@ -48,7 +49,7 @@ namespace MediaBrowser.Controller.IO
         /// <param name="path">The path.</param>
         public void TemporarilyIgnore(string path)
         {
-            TempIgnoredPaths[path] = path;
+            _tempIgnoredPaths[path] = path;
         }
 
         /// <summary>
@@ -58,7 +59,7 @@ namespace MediaBrowser.Controller.IO
         public void RemoveTempIgnore(string path)
         {
             string val;
-            TempIgnoredPaths.TryRemove(path, out val);
+            _tempIgnoredPaths.TryRemove(path, out val);
         }
 
         /// <summary>
@@ -95,7 +96,7 @@ namespace MediaBrowser.Controller.IO
         /// <summary>
         /// Starts this instance.
         /// </summary>
-        internal void Start()
+        public void Start()
         {
             LibraryManager.LibraryChanged += Instance_LibraryChanged;
 
@@ -178,7 +179,7 @@ namespace MediaBrowser.Controller.IO
                 try
                 {
                     newWatcher.EnableRaisingEvents = true;
-                    FileSystemWatchers.Add(newWatcher);
+                    _fileSystemWatchers.Add(newWatcher);
 
                     Logger.Info("Watching directory " + path);
                 }
@@ -199,7 +200,7 @@ namespace MediaBrowser.Controller.IO
         /// <param name="path">The path.</param>
         private void StopWatchingPath(string path)
         {
-            var watcher = FileSystemWatchers.FirstOrDefault(f => f.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
+            var watcher = _fileSystemWatchers.FirstOrDefault(f => f.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
 
             if (watcher != null)
             {
@@ -218,18 +219,18 @@ namespace MediaBrowser.Controller.IO
             watcher.EnableRaisingEvents = false;
             watcher.Dispose();
 
-            var watchers = FileSystemWatchers.ToList();
+            var watchers = _fileSystemWatchers.ToList();
 
             watchers.Remove(watcher);
 
-            FileSystemWatchers = new ConcurrentBag<FileSystemWatcher>(watchers);
+            _fileSystemWatchers = new ConcurrentBag<FileSystemWatcher>(watchers);
         }
 
         /// <summary>
         /// Handles the LibraryChanged event of the Kernel
         /// </summary>
         /// <param name="sender">The source of the event.</param>
-        /// <param name="e">The <see cref="Library.ChildrenChangedEventArgs" /> instance containing the event data.</param>
+        /// <param name="e">The <see cref="MediaBrowser.Controller.Library.ChildrenChangedEventArgs" /> instance containing the event data.</param>
         void Instance_LibraryChanged(object sender, ChildrenChangedEventArgs e)
         {
             if (e.Folder is AggregateFolder && e.HasAddOrRemoveChange)
@@ -316,7 +317,7 @@ namespace MediaBrowser.Controller.IO
             {
                 return;
             }
-            if (TempIgnoredPaths.ContainsKey(e.FullPath))
+            if (_tempIgnoredPaths.ContainsKey(e.FullPath))
             {
                 Logger.Info("Watcher requested to ignore change to " + e.FullPath);
                 return;
@@ -327,17 +328,17 @@ namespace MediaBrowser.Controller.IO
             //Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path
             var affectedPath = e.FullPath;
 
-            affectedPaths.AddOrUpdate(affectedPath, affectedPath, (key, oldValue) => affectedPath);
+            _affectedPaths.AddOrUpdate(affectedPath, affectedPath, (key, oldValue) => affectedPath);
 
-            lock (timerLock)
+            lock (_timerLock)
             {
-                if (updateTimer == null)
+                if (_updateTimer == null)
                 {
-                    updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
                 }
                 else
                 {
-                    updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
                 }
             }
         }
@@ -348,24 +349,24 @@ namespace MediaBrowser.Controller.IO
         /// <param name="stateInfo">The state info.</param>
         private async void TimerStopped(object stateInfo)
         {
-            lock (timerLock)
+            lock (_timerLock)
             {
                 // Extend the timer as long as any of the paths are still being written to.
-                if (affectedPaths.Any(p => IsFileLocked(p.Key)))
+                if (_affectedPaths.Any(p => IsFileLocked(p.Key)))
                 {
                     Logger.Info("Timer extended.");
-                    updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
+                    _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
                     return;
                 }
 
                 Logger.Info("Timer stopped.");
 
-                updateTimer.Dispose();
-                updateTimer = null;
+                _updateTimer.Dispose();
+                _updateTimer = null;
             }
 
-            var paths = affectedPaths.Keys.ToList();
-            affectedPaths.Clear();
+            var paths = _affectedPaths.Keys.ToList();
+            _affectedPaths.Clear();
 
             await ProcessPathChanges(paths).ConfigureAwait(false);
         }
@@ -493,29 +494,29 @@ namespace MediaBrowser.Controller.IO
         /// <summary>
         /// Stops this instance.
         /// </summary>
-        private void Stop()
+        public void Stop()
         {
             LibraryManager.LibraryChanged -= Instance_LibraryChanged;
 
             FileSystemWatcher watcher;
 
-            while (FileSystemWatchers.TryTake(out watcher))
+            while (_fileSystemWatchers.TryTake(out watcher))
             {
                 watcher.Changed -= watcher_Changed;
                 watcher.EnableRaisingEvents = false;
                 watcher.Dispose();
             }
 
-            lock (timerLock)
+            lock (_timerLock)
             {
-                if (updateTimer != null)
+                if (_updateTimer != null)
                 {
-                    updateTimer.Dispose();
-                    updateTimer = null;
+                    _updateTimer.Dispose();
+                    _updateTimer = null;
                 }
             } 
 
-            affectedPaths.Clear();
+            _affectedPaths.Clear();
         }
 
         /// <summary>

+ 2 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -111,6 +111,7 @@
     <Compile Include="HttpServer\ServerFactory.cs" />
     <Compile Include="HttpServer\StreamWriter.cs" />
     <Compile Include="HttpServer\SwaggerService.cs" />
+    <Compile Include="IO\DirectoryWatchers.cs" />
     <Compile Include="Library\CoreResolutionIgnoreRule.cs" />
     <Compile Include="Library\LibraryManager.cs" />
     <Compile Include="Library\ResolverHelper.cs" />
@@ -128,6 +129,7 @@
     <Compile Include="Library\Resolvers\VideoResolver.cs" />
     <Compile Include="Library\UserManager.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Providers\ProviderManager.cs" />
     <Compile Include="Reflection\TypeMapper.cs" />
     <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
     <Compile Include="ScheduledTasks\ChapterImagesTask.cs" />

+ 158 - 43
MediaBrowser.Controller/Providers/ProviderManager.cs → MediaBrowser.Server.Implementations/Providers/ProviderManager.cs

@@ -1,9 +1,11 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Concurrent;
@@ -13,12 +15,12 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace MediaBrowser.Controller.Providers
+namespace MediaBrowser.Server.Implementations.Providers
 {
     /// <summary>
     /// Class ProviderManager
     /// </summary>
-    public class ProviderManager : IDisposable
+    public class ProviderManager : IProviderManager
     {
         /// <summary>
         /// The remote image cache
@@ -41,23 +43,37 @@ namespace MediaBrowser.Controller.Providers
         /// </summary>
         private readonly IHttpClient _httpClient;
 
+        /// <summary>
+        /// The _directory watchers
+        /// </summary>
+        private readonly IDirectoryWatchers _directoryWatchers;
+
+        /// <summary>
+        /// Gets or sets the configuration manager.
+        /// </summary>
+        /// <value>The configuration manager.</value>
         private IServerConfigurationManager ConfigurationManager { get; set; }
 
-        private Kernel Kernel { get; set; }
+        /// <summary>
+        /// Gets the list of currently registered metadata prvoiders
+        /// </summary>
+        /// <value>The metadata providers enumerable.</value>
+        private BaseMetadataProvider[] MetadataProviders { get; set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ProviderManager" /> class.
         /// </summary>
-        /// <param name="kernel">The kernel.</param>
         /// <param name="httpClient">The HTTP client.</param>
-        /// <param name="logger">The logger.</param>
-        public ProviderManager(Kernel kernel, IHttpClient httpClient, ILogger logger, IServerConfigurationManager configurationManager)
+        /// <param name="configurationManager">The configuration manager.</param>
+        /// <param name="directoryWatchers">The directory watchers.</param>
+        /// <param name="logManager">The log manager.</param>
+        public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager)
         {
-            _logger = logger;
-            Kernel = kernel;
+            _logger = logManager.GetLogger("ProviderManager");
             _httpClient = httpClient;
             ConfigurationManager = configurationManager;
-            _remoteImageCache = new FileSystemRepository(ImagesDataPath);
+            _directoryWatchers = directoryWatchers;
+            _remoteImageCache = new FileSystemRepository(configurationManager.ApplicationPaths.DownloadedImagesDataPath);
 
             configurationManager.ConfigurationUpdated += configurationManager_ConfigurationUpdated;
         }
@@ -77,37 +93,20 @@ namespace MediaBrowser.Controller.Providers
         }
 
         /// <summary>
-        /// The _images data path
+        /// Gets or sets the supported providers key.
         /// </summary>
-        private string _imagesDataPath;
+        /// <value>The supported providers key.</value>
+        private Guid SupportedProvidersKey { get; set; }
+
         /// <summary>
-        /// Gets the images data path.
+        /// Adds the metadata providers.
         /// </summary>
-        /// <value>The images data path.</value>
-        public string ImagesDataPath
+        /// <param name="providers">The providers.</param>
+        public void AddMetadataProviders(IEnumerable<BaseMetadataProvider> providers)
         {
-            get
-            {
-                if (_imagesDataPath == null)
-                {
-                    _imagesDataPath = Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "remote-images");
-
-                    if (!Directory.Exists(_imagesDataPath))
-                    {
-                        Directory.CreateDirectory(_imagesDataPath);
-                    }
-                }
-
-                return _imagesDataPath;
-            }
+            MetadataProviders = providers.ToArray();
         }
 
-        /// <summary>
-        /// Gets or sets the supported providers key.
-        /// </summary>
-        /// <value>The supported providers key.</value>
-        private Guid SupportedProvidersKey { get; set; }
-
         /// <summary>
         /// Runs all metadata providers for an entity, and returns true or false indicating if at least one was refreshed and requires persistence
         /// </summary>
@@ -116,7 +115,7 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="force">if set to <c>true</c> [force].</param>
         /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
         /// <returns>Task{System.Boolean}.</returns>
-        internal async Task<bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true)
+        public async Task<bool> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true)
         {
             // Allow providers of the same priority to execute in parallel
             MetadataProviderPriority? currentPriority = null;
@@ -127,7 +126,7 @@ namespace MediaBrowser.Controller.Providers
             cancellationToken.ThrowIfCancellationRequested();
 
             // Determine if supported providers have changed
-            var supportedProviders = Kernel.MetadataProviders.Where(p => p.Supports(item)).ToList();
+            var supportedProviders = MetadataProviders.Where(p => p.Supports(item)).ToList();
 
             BaseProviderInfo supportedProvidersInfo;
 
@@ -201,7 +200,7 @@ namespace MediaBrowser.Controller.Providers
                     continue;
                 }
 
-                currentTasks.Add(provider.FetchAsync(item, force, cancellationToken));
+                currentTasks.Add(FetchAsync(provider, item, force, cancellationToken));
                 currentPriority = provider.Priority;
             }
 
@@ -219,13 +218,69 @@ namespace MediaBrowser.Controller.Providers
             return result || providersChanged;
         }
 
+        /// <summary>
+        /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
+        /// </summary>
+        /// <param name="provider">The provider.</param>
+        /// <param name="item">The item.</param>
+        /// <param name="force">if set to <c>true</c> [force].</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{System.Boolean}.</returns>
+        /// <exception cref="System.ArgumentNullException"></exception>
+        private async Task<bool> FetchAsync(BaseMetadataProvider provider, BaseItem item, bool force, CancellationToken cancellationToken)
+        {
+            if (item == null)
+            {
+                throw new ArgumentNullException();
+            }
+
+            cancellationToken.ThrowIfCancellationRequested();
+
+            _logger.Info("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name ?? "--Unknown--");
+
+            // This provides the ability to cancel just this one provider
+            var innerCancellationTokenSource = new CancellationTokenSource();
+
+            OnProviderRefreshBeginning(provider, item, innerCancellationTokenSource);
+
+            try
+            {
+                return await provider.FetchAsync(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token).ConfigureAwait(false);
+            }
+            catch (OperationCanceledException ex)
+            {
+                _logger.Info("{0} cancelled for {1}", provider.GetType().Name, item.Name);
+
+                // If the outer cancellation token is the one that caused the cancellation, throw it
+                if (cancellationToken.IsCancellationRequested && ex.CancellationToken == cancellationToken)
+                {
+                    throw;
+                }
+
+                return false;
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("{0} failed refreshing {1}", ex, provider.GetType().Name, item.Name);
+
+                provider.SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.Failure);
+                return true;
+            }
+            finally
+            {
+                innerCancellationTokenSource.Dispose();
+
+                OnProviderRefreshCompleted(provider, item);
+            }
+        }
+
         /// <summary>
         /// Notifies the kernal that a provider has begun refreshing
         /// </summary>
         /// <param name="provider">The provider.</param>
         /// <param name="item">The item.</param>
         /// <param name="cancellationTokenSource">The cancellation token source.</param>
-        internal void OnProviderRefreshBeginning(BaseMetadataProvider provider, BaseItem item, CancellationTokenSource cancellationTokenSource)
+        public void OnProviderRefreshBeginning(BaseMetadataProvider provider, BaseItem item, CancellationTokenSource cancellationTokenSource)
         {
             var key = item.Id + provider.GetType().Name;
 
@@ -253,7 +308,7 @@ namespace MediaBrowser.Controller.Providers
         /// </summary>
         /// <param name="provider">The provider.</param>
         /// <param name="item">The item.</param>
-        internal void OnProviderRefreshCompleted(BaseMetadataProvider provider, BaseItem item)
+        public void OnProviderRefreshCompleted(BaseMetadataProvider provider, BaseItem item)
         {
             var key = item.Id + provider.GetType().Name;
 
@@ -268,7 +323,7 @@ namespace MediaBrowser.Controller.Providers
         /// <summary>
         /// Validates the currently running providers and cancels any that should not be run due to configuration changes
         /// </summary>
-        internal void ValidateCurrentlyRunningProviders()
+        private void ValidateCurrentlyRunningProviders()
         {
             _logger.Info("Validing currently running providers");
 
@@ -321,7 +376,7 @@ namespace MediaBrowser.Controller.Providers
 
             if (ConfigurationManager.Configuration.SaveLocalMeta) // queue to media directories
             {
-                await Kernel.FileSystemManager.SaveToLibraryFilesystem(item, localPath, img, cancellationToken).ConfigureAwait(false);
+                await SaveToLibraryFilesystem(item, localPath, img, cancellationToken).ConfigureAwait(false);
             }
             else
             {
@@ -352,6 +407,63 @@ namespace MediaBrowser.Controller.Providers
             return localPath;
         }
 
+
+        /// <summary>
+        /// Saves to library filesystem.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="path">The path.</param>
+        /// <param name="dataToSave">The data to save.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        /// <exception cref="System.ArgumentNullException"></exception>
+        public async Task SaveToLibraryFilesystem(BaseItem item, string path, Stream dataToSave, CancellationToken cancellationToken)
+        {
+            if (item == null)
+            {
+                throw new ArgumentNullException();
+            }
+            if (string.IsNullOrEmpty(path))
+            {
+                throw new ArgumentNullException();
+            }
+            if (dataToSave == null)
+            {
+                throw new ArgumentNullException();
+            }
+            if (cancellationToken == null)
+            {
+                throw new ArgumentNullException();
+            }
+
+            cancellationToken.ThrowIfCancellationRequested();
+
+            //Tell the watchers to ignore
+            _directoryWatchers.TemporarilyIgnore(path);
+
+            //Make the mod
+
+            dataToSave.Position = 0;
+
+            try
+            {
+                using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
+                {
+                    await dataToSave.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
+
+                    dataToSave.Dispose();
+
+                    // If this is ever used for something other than metadata we can add a file type param
+                    item.ResolveArgs.AddMetadataFile(path);
+                }
+            }
+            finally
+            {
+                //Remove the ignore
+                _directoryWatchers.RemoveTempIgnore(path);
+            }
+        }
+
         /// <summary>
         /// Releases unmanaged and - optionally - managed resources.
         /// </summary>
@@ -364,6 +476,9 @@ namespace MediaBrowser.Controller.Providers
             }
         }
 
+        /// <summary>
+        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+        /// </summary>
         public void Dispose()
         {
             Dispose(true);

+ 6 - 2
MediaBrowser.Server.Implementations/ScheduledTasks/ImageCleanupTask.cs

@@ -27,17 +27,21 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         /// </summary>
         private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
+        private readonly IServerApplicationPaths _appPaths;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageCleanupTask" /> class.
         /// </summary>
         /// <param name="kernel">The kernel.</param>
         /// <param name="logger">The logger.</param>
-        public ImageCleanupTask(Kernel kernel, ILogger logger, ILibraryManager libraryManager)
+        /// <param name="libraryManager">The library manager.</param>
+        /// <param name="appPaths">The app paths.</param>
+        public ImageCleanupTask(Kernel kernel, ILogger logger, ILibraryManager libraryManager, IServerApplicationPaths appPaths)
         {
             _kernel = kernel;
             _logger = logger;
             _libraryManager = libraryManager;
+            _appPaths = appPaths;
         }
 
         /// <summary>
@@ -65,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
             // First gather all image files
             var files = GetFiles(_kernel.FFMpegManager.AudioImagesDataPath)
                 .Concat(GetFiles(_kernel.FFMpegManager.VideoImagesDataPath))
-                .Concat(GetFiles(_kernel.ProviderManager.ImagesDataPath))
+                .Concat(GetFiles(_appPaths.DownloadedImagesDataPath))
                 .ToList();
 
             // Now gather all items

+ 27 - 1
MediaBrowser.Server.Implementations/ServerApplicationPaths.cs

@@ -302,7 +302,7 @@ namespace MediaBrowser.Server.Implementations
         /// Gets the FF MPEG stream cache path.
         /// </summary>
         /// <value>The FF MPEG stream cache path.</value>
-        public string FFMpegStreamCachePath
+        public string EncodedMediaCachePath
         {
             get
             {
@@ -345,5 +345,31 @@ namespace MediaBrowser.Server.Implementations
                 return _mediaToolsPath;
             }
         }
+
+        /// <summary>
+        /// The _images data path
+        /// </summary>
+        private string _downloadedImagesDataPath;
+        /// <summary>
+        /// Gets the images data path.
+        /// </summary>
+        /// <value>The images data path.</value>
+        public string DownloadedImagesDataPath
+        {
+            get
+            {
+                if (_downloadedImagesDataPath == null)
+                {
+                    _downloadedImagesDataPath = Path.Combine(DataPath, "remote-images");
+
+                    if (!Directory.Exists(_downloadedImagesDataPath))
+                    {
+                        Directory.CreateDirectory(_downloadedImagesDataPath);
+                    }
+                }
+
+                return _downloadedImagesDataPath;
+            }
+        }
     }
 }

+ 2 - 2
MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs

@@ -104,8 +104,8 @@ namespace MediaBrowser.Server.Implementations.ServerManager
         /// <value>The web socket listeners.</value>
         private readonly List<IWebSocketListener> _webSocketListeners = new List<IWebSocketListener>();
 
-        private Kernel _kernel;
-
+        private readonly Kernel _kernel;
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerManager" /> class.
         /// </summary>

+ 4 - 4
MediaBrowser.ServerApplication/App.xaml.cs

@@ -156,13 +156,13 @@ namespace MediaBrowser.ServerApplication
             {
                 CompositionRoot = new ApplicationHost();
 
-                var win = new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager);
-
                 Logger = CompositionRoot.LogManager.GetLogger("App");
 
-                win.Show();
-
                 await CompositionRoot.Init();
+
+                var win = new MainWindow(CompositionRoot.LogManager, CompositionRoot, CompositionRoot.ServerConfigurationManager, CompositionRoot.UserManager, CompositionRoot.LibraryManager, CompositionRoot.JsonSerializer);
+
+                win.Show();
             }
             catch (Exception ex)
             {

+ 124 - 19
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -2,18 +2,24 @@
 using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Constants;
-using MediaBrowser.Common.Events;
 using MediaBrowser.Common.Implementations;
 using MediaBrowser.Common.Implementations.ScheduledTasks;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Updates;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.MediaInfo;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Resolvers;
 using MediaBrowser.Controller.Updates;
+using MediaBrowser.Controller.Weather;
 using MediaBrowser.IsoMounter;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
@@ -23,7 +29,9 @@ using MediaBrowser.Server.Implementations;
 using MediaBrowser.Server.Implementations.BdInfo;
 using MediaBrowser.Server.Implementations.Configuration;
 using MediaBrowser.Server.Implementations.HttpServer;
+using MediaBrowser.Server.Implementations.IO;
 using MediaBrowser.Server.Implementations.Library;
+using MediaBrowser.Server.Implementations.Providers;
 using MediaBrowser.Server.Implementations.ServerManager;
 using MediaBrowser.Server.Implementations.Udp;
 using MediaBrowser.Server.Implementations.Updates;
@@ -78,23 +86,73 @@ namespace MediaBrowser.ServerApplication
             return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer);
         }
 
+        /// <summary>
+        /// Gets or sets the installation manager.
+        /// </summary>
+        /// <value>The installation manager.</value>
         private IInstallationManager InstallationManager { get; set; }
+        /// <summary>
+        /// Gets or sets the server manager.
+        /// </summary>
+        /// <value>The server manager.</value>
         private IServerManager ServerManager { get; set; }
-
+        /// <summary>
+        /// Gets or sets the user manager.
+        /// </summary>
+        /// <value>The user manager.</value>
+        public IUserManager UserManager { get; set; }
+        /// <summary>
+        /// Gets or sets the library manager.
+        /// </summary>
+        /// <value>The library manager.</value>
+        internal ILibraryManager LibraryManager { get; set; }
+        /// <summary>
+        /// Gets or sets the directory watchers.
+        /// </summary>
+        /// <value>The directory watchers.</value>
+        private IDirectoryWatchers DirectoryWatchers { get; set; }
+        /// <summary>
+        /// Gets or sets the provider manager.
+        /// </summary>
+        /// <value>The provider manager.</value>
+        private IProviderManager ProviderManager { get; set; }
+        /// <summary>
+        /// Gets or sets the zip client.
+        /// </summary>
+        /// <value>The zip client.</value>
+        private IZipClient ZipClient { get; set; }
+        /// <summary>
+        /// Gets or sets the HTTP server.
+        /// </summary>
+        /// <value>The HTTP server.</value>
+        private IHttpServer HttpServer { get; set; }
+ 
+        /// <summary>
+        /// Inits this instance.
+        /// </summary>
+        /// <returns>Task.</returns>
         public override async Task Init()
         {
             await base.Init().ConfigureAwait(false);
 
-            await ServerKernel.Init().ConfigureAwait(false);
+            Task.Run(async () =>
+            {
+                await ServerKernel.LoadRepositories(ServerConfigurationManager).ConfigureAwait(false);
+
+                DirectoryWatchers.Start();
+
+                Parallel.ForEach(GetExports<IServerEntryPoint>(), entryPoint => entryPoint.Run());
+            });
         }
 
         /// <summary>
         /// Registers resources that classes will depend on
         /// </summary>
+        /// <returns>Task.</returns>
         protected override async Task RegisterResources()
         {
-            ServerKernel = new Kernel(this, XmlSerializer, LogManager, ServerConfigurationManager);
-            
+            ServerKernel = new Kernel(ServerConfigurationManager);
+
             await base.RegisterResources().ConfigureAwait(false);
 
             RegisterSingleInstance<IServerApplicationHost>(this);
@@ -108,20 +166,66 @@ namespace MediaBrowser.ServerApplication
 
             RegisterSingleInstance<IIsoManager>(new PismoIsoManager(Logger));
             RegisterSingleInstance<IBlurayExaminer>(new BdInfoExaminer());
-            RegisterSingleInstance<IZipClient>(new DotNetZipClient());
-            RegisterSingleInstance(ServerFactory.CreateServer(this, ProtobufSerializer, Logger, "Media Browser", "index.html"), false);
+
+            ZipClient = new DotNetZipClient();
+            RegisterSingleInstance(ZipClient);
+
+            HttpServer = ServerFactory.CreateServer(this, ProtobufSerializer, Logger, "Media Browser", "index.html");
+            RegisterSingleInstance(HttpServer, false);
 
             ServerManager = new ServerManager(this, NetworkManager, JsonSerializer, Logger, ServerConfigurationManager, ServerKernel);
             RegisterSingleInstance(ServerManager);
 
-            var userManager = new UserManager(ServerKernel, Logger, ServerConfigurationManager);
+            UserManager = new UserManager(ServerKernel, Logger, ServerConfigurationManager);
+            RegisterSingleInstance(UserManager);
 
-            RegisterSingleInstance<IUserManager>(userManager);
-
-            RegisterSingleInstance<ILibraryManager>(new LibraryManager(ServerKernel, Logger, TaskManager, userManager, ServerConfigurationManager));
+            LibraryManager = new LibraryManager(ServerKernel, Logger, TaskManager, UserManager, ServerConfigurationManager);
+            RegisterSingleInstance(LibraryManager);
 
             InstallationManager = new InstallationManager(HttpClient, PackageManager, JsonSerializer, Logger, this);
             RegisterSingleInstance(InstallationManager);
+
+            DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager);
+            RegisterSingleInstance(DirectoryWatchers);
+
+            ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager);
+            RegisterSingleInstance(ProviderManager);
+
+            SetKernelProperties();
+            SetStaticProperties();
+        }
+
+        /// <summary>
+        /// Sets the kernel properties.
+        /// </summary>
+        private void SetKernelProperties()
+        {
+            ServerKernel.FFMpegManager = new FFMpegManager(ServerKernel, ZipClient, JsonSerializer, ProtobufSerializer, LogManager, ApplicationPaths);
+            ServerKernel.ImageManager = new ImageManager(ServerKernel, ProtobufSerializer, LogManager.GetLogger("ImageManager"), ApplicationPaths);
+
+            ServerKernel.UserDataRepositories = GetExports<IUserDataRepository>();
+            ServerKernel.UserRepositories = GetExports<IUserRepository>();
+            ServerKernel.DisplayPreferencesRepositories = GetExports<IDisplayPreferencesRepository>();
+            ServerKernel.ItemRepositories = GetExports<IItemRepository>();
+            ServerKernel.WeatherProviders = GetExports<IWeatherProvider>();
+            ServerKernel.ImageEnhancers = GetExports<IImageEnhancer>().OrderBy(e => e.Priority).ToArray();
+            ServerKernel.StringFiles = GetExports<LocalizedStringData>();
+        }
+
+        /// <summary>
+        /// Dirty hacks
+        /// </summary>
+        private void SetStaticProperties()
+        {
+            // For now there's no real way to inject these properly
+            BaseItem.Logger = LogManager.GetLogger("BaseItem");
+            BaseItem.ConfigurationManager = ServerConfigurationManager;
+            BaseItem.LibraryManager = LibraryManager;
+            BaseItem.ProviderManager = ProviderManager;
+            User.XmlSerializer = XmlSerializer;
+            User.UserManager = UserManager;
+            Ratings.ConfigurationManager = ServerConfigurationManager;
+            LocalizedStrings.ApplicationPaths = ApplicationPaths;
         }
 
         /// <summary>
@@ -131,12 +235,14 @@ namespace MediaBrowser.ServerApplication
         {
             base.FindParts();
 
-            Resolve<IHttpServer>().Init(GetExports<IRestfulService>(false));
-            Resolve<IServerManager>().AddWebSocketListeners(GetExports<IWebSocketListener>(false));
+            HttpServer.Init(GetExports<IRestfulService>(false));
+
+            ServerManager.AddWebSocketListeners(GetExports<IWebSocketListener>(false));
+            ServerManager.Start();
 
-            Resolve<IServerManager>().Start();
+            LibraryManager.AddParts(GetExports<IResolverIgnoreRule>(), GetExports<IVirtualFolderCreator>(), GetExports<IItemResolver>(), GetExports<IIntroProvider>());
 
-            Resolve<ILibraryManager>().AddParts(GetExports<IResolverIgnoreRule>(), GetExports<IVirtualFolderCreator>(), GetExports<IItemResolver>(), GetExports<IIntroProvider>());
+            ProviderManager.AddMetadataProviders(GetExports<BaseMetadataProvider>().OrderBy(e => e.Priority).ToArray());
         }
 
         /// <summary>
@@ -164,9 +270,8 @@ namespace MediaBrowser.ServerApplication
         /// <returns>Task{CheckForUpdateResult}.</returns>
         public async override Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
         {
-            var pkgManager = Resolve<IPackageManager>();
-            var availablePackages = await pkgManager.GetAvailablePackages(CancellationToken.None).ConfigureAwait(false);
-            var version = Resolve<IInstallationManager>().GetLatestCompatibleVersion(availablePackages, Constants.MBServerPkgName, ConfigurationManager.CommonConfiguration.SystemUpdateLevel);
+            var availablePackages = await PackageManager.GetAvailablePackages(CancellationToken.None).ConfigureAwait(false);
+            var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, Constants.MBServerPkgName, ConfigurationManager.CommonConfiguration.SystemUpdateLevel);
 
             return version != null ? new CheckForUpdateResult { AvailableVersion = version.version, IsUpdateAvailable = version.version > ApplicationVersion, Package = version } :
                        new CheckForUpdateResult { AvailableVersion = ApplicationVersion, IsUpdateAvailable = false };

+ 1 - 1
MediaBrowser.ServerApplication/NewItemNotifier.cs → MediaBrowser.ServerApplication/EntryPoints/NewItemNotifier.cs

@@ -9,7 +9,7 @@ using System.Linq;
 using System.Threading;
 using System.Windows.Controls.Primitives;
 
-namespace MediaBrowser.ServerApplication
+namespace MediaBrowser.ServerApplication.EntryPoints
 {
     /// <summary>
     /// Class NewItemNotifier

+ 41 - 0
MediaBrowser.ServerApplication/EntryPoints/RefreshUsersMetadata.cs

@@ -0,0 +1,41 @@
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Plugins;
+using System.Threading;
+
+namespace MediaBrowser.ServerApplication.EntryPoints
+{
+    /// <summary>
+    /// Class RefreshUsersMetadata
+    /// </summary>
+    public class RefreshUsersMetadata : IServerEntryPoint
+    {
+        /// <summary>
+        /// The _user manager
+        /// </summary>
+        private readonly IUserManager _userManager;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
+        /// </summary>
+        /// <param name="userManager">The user manager.</param>
+        public RefreshUsersMetadata(IUserManager userManager)
+        {
+            _userManager = userManager;
+        }
+
+        /// <summary>
+        /// Runs this instance.
+        /// </summary>
+        public async void Run()
+        {
+            await _userManager.RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false);
+        }
+
+        /// <summary>
+        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+        /// </summary>
+        public void Dispose()
+        {
+        }
+    }
+}

+ 1 - 1
MediaBrowser.ServerApplication/StartupWizard.cs → MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs

@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Plugins;
 using System.Linq;
 
-namespace MediaBrowser.ServerApplication
+namespace MediaBrowser.ServerApplication.EntryPoints
 {
     /// <summary>
     /// Class StartupWizard

+ 1 - 1
MediaBrowser.ServerApplication/WebSocketEvents.cs → MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs

@@ -12,7 +12,7 @@ using MediaBrowser.Model.Tasks;
 using MediaBrowser.Model.Updates;
 using System;
 
-namespace MediaBrowser.ServerApplication
+namespace MediaBrowser.ServerApplication.EntryPoints
 {
     /// <summary>
     /// Class WebSocketEvents

+ 13 - 6
MediaBrowser.ServerApplication/MainWindow.xaml.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
 using MediaBrowser.ServerApplication.Logging;
 using System;
 using System.ComponentModel;
@@ -38,7 +39,11 @@ namespace MediaBrowser.ServerApplication
         /// The _configuration manager
         /// </summary>
         private readonly IServerConfigurationManager _configurationManager;
-        
+
+        private readonly IUserManager _userManager;
+        private readonly ILibraryManager _libraryManager;
+        private readonly IJsonSerializer _jsonSerializer;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="MainWindow" /> class.
         /// </summary>
@@ -46,7 +51,7 @@ namespace MediaBrowser.ServerApplication
         /// <param name="logger">The logger.</param>
         /// <param name="appHost">The app host.</param>
         /// <exception cref="System.ArgumentNullException">logger</exception>
-        public MainWindow(ILogManager logManager, IApplicationHost appHost, IServerConfigurationManager configurationManager)
+        public MainWindow(ILogManager logManager, IApplicationHost appHost, IServerConfigurationManager configurationManager, IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer)
         {
             if (logManager == null)
             {
@@ -65,6 +70,9 @@ namespace MediaBrowser.ServerApplication
             _appHost = appHost;
             _logManager = logManager;
             _configurationManager = configurationManager;
+            _userManager = userManager;
+            _libraryManager = libraryManager;
+            _jsonSerializer = jsonSerializer;
 
             InitializeComponent();
 
@@ -209,8 +217,7 @@ namespace MediaBrowser.ServerApplication
         /// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
         private void cmOpenExplorer_click(object sender, RoutedEventArgs e)
         {
-            var explorer = (LibraryExplorer)_appHost.CreateInstance(typeof(LibraryExplorer));
-            explorer.Show();
+            new LibraryExplorer(_jsonSerializer, _logger, _appHost, _userManager, _libraryManager).Show();
         }
 
         /// <summary>
@@ -220,7 +227,7 @@ namespace MediaBrowser.ServerApplication
         /// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
         private void cmOpenDashboard_click(object sender, RoutedEventArgs e)
         {
-            var user = _appHost.Resolve<IUserManager>().Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
+            var user = _userManager.Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
             OpenDashboard(user);
         }
 
@@ -249,7 +256,7 @@ namespace MediaBrowser.ServerApplication
         /// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
         private void cmdBrowseLibrary_click(object sender, RoutedEventArgs e)
         {
-            var user = _appHost.Resolve<IUserManager>().Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
+            var user = _userManager.Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
             App.OpenDashboardPage("index.html", user, _configurationManager);
         }
 

+ 8 - 7
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -158,9 +158,10 @@
     <Reference Include="PresentationFramework" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="NewItemNotifier.cs" />
-    <Compile Include="StartupWizard.cs" />
-    <Compile Include="WebSocketEvents.cs" />
+    <Compile Include="EntryPoints\NewItemNotifier.cs" />
+    <Compile Include="EntryPoints\RefreshUsersMetadata.cs" />
+    <Compile Include="EntryPoints\StartupWizard.cs" />
+    <Compile Include="EntryPoints\WebSocketEvents.cs" />
     <Page Include="App.xaml">
       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
@@ -403,9 +404,9 @@ del "$(SolutionDir)..\Deploy\MBServer.zip"
   </Target>
   -->
   <Target Name="AfterBuild">
-      <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
-         <Output TaskParameter="Assemblies" ItemName="CurrentAssembly" />
-      </GetAssemblyIdentity>
-      <Exec Command="copy $(SolutionDir)..\Deploy\MBServer.zip  $(SolutionDir)..\Deploy\MBServer_%(CurrentAssembly.Version).zip /y" Condition="'$(ConfigurationName)' == 'Release'"/>
+    <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
+      <Output TaskParameter="Assemblies" ItemName="CurrentAssembly" />
+    </GetAssemblyIdentity>
+    <Exec Command="copy $(SolutionDir)..\Deploy\MBServer.zip  $(SolutionDir)..\Deploy\MBServer_%(CurrentAssembly.Version).zip /y" Condition="'$(ConfigurationName)' == 'Release'" />
   </Target>
 </Project>

+ 3 - 0
MediaBrowser.sln

@@ -237,4 +237,7 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 EndGlobal