소스 검색

One async call leads to another, and another, all the way up the call stack...

LukePulverenti Luke Pulverenti luke pulverenti 13 년 전
부모
커밋
937d27ae9d
38개의 변경된 파일386개의 추가작업 그리고 366개의 파일을 삭제
  1. 18 13
      MediaBrowser.Api/ApiService.cs
  2. 5 9
      MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs
  3. 5 4
      MediaBrowser.Api/HttpHandlers/GenreHandler.cs
  4. 6 21
      MediaBrowser.Api/HttpHandlers/GenresHandler.cs
  5. 47 59
      MediaBrowser.Api/HttpHandlers/ImageHandler.cs
  6. 3 2
      MediaBrowser.Api/HttpHandlers/ItemHandler.cs
  7. 4 3
      MediaBrowser.Api/HttpHandlers/ItemListHandler.cs
  8. 5 4
      MediaBrowser.Api/HttpHandlers/PersonHandler.cs
  9. 7 3
      MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs
  10. 19 15
      MediaBrowser.Api/HttpHandlers/PluginsHandler.cs
  11. 5 4
      MediaBrowser.Api/HttpHandlers/StudioHandler.cs
  12. 6 21
      MediaBrowser.Api/HttpHandlers/StudiosHandler.cs
  13. 6 2
      MediaBrowser.Api/HttpHandlers/UsersHandler.cs
  14. 5 4
      MediaBrowser.Api/HttpHandlers/YearHandler.cs
  15. 6 21
      MediaBrowser.Api/HttpHandlers/YearsHandler.cs
  16. 26 2
      MediaBrowser.Common/Kernel/BaseKernel.cs
  17. 3 3
      MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs
  18. 23 38
      MediaBrowser.Common/Net/Handlers/BaseHandler.cs
  19. 14 23
      MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs
  20. 26 30
      MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs
  21. 3 6
      MediaBrowser.Controller/IO/DirectoryWatchers.cs
  22. 42 0
      MediaBrowser.Controller/Kernel.cs
  23. 33 27
      MediaBrowser.Controller/Library/ItemController.cs
  24. 3 2
      MediaBrowser.Controller/Providers/AudioInfoProvider.cs
  25. 18 5
      MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
  26. 2 2
      MediaBrowser.Controller/Providers/FolderProviderFromXml.cs
  27. 7 2
      MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs
  28. 9 2
      MediaBrowser.Controller/Providers/LocalTrailerProvider.cs
  29. 6 18
      MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
  30. 2 2
      MediaBrowser.Controller/Resolvers/FolderResolver.cs
  31. 2 2
      MediaBrowser.Controller/Resolvers/VirtualFolderResolver.cs
  32. 1 1
      MediaBrowser.Controller/Xml/BaseItemXmlParser.cs
  33. 2 2
      MediaBrowser.Movies/Providers/MovieProviderFromXml.cs
  34. 2 2
      MediaBrowser.Movies/Resolvers/MovieResolver.cs
  35. 1 2
      MediaBrowser.ServerApplication/MainWindow.xaml.cs
  36. 6 4
      MediaBrowser.TV/Providers/EpisodeImageFromMediaLocationProvider.cs
  37. 6 4
      MediaBrowser.TV/Providers/EpisodeProviderFromXml.cs
  38. 2 2
      MediaBrowser.TV/Providers/SeriesProviderFromXml.cs

+ 18 - 13
MediaBrowser.Api/ApiService.cs

@@ -1,8 +1,7 @@
 using System;
 using System;
-using System.IO;
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
-using System.Reflection;
-using MediaBrowser.Common.Configuration;
+using System.Threading.Tasks;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
@@ -21,7 +20,7 @@ namespace MediaBrowser.Api
             return Kernel.Instance.GetItemById(guid);
             return Kernel.Instance.GetItemById(guid);
         }
         }
 
 
-        public static DTOBaseItem GetDTOBaseItem(BaseItem item, User user, 
+        public async static Task<DTOBaseItem> GetDTOBaseItem(BaseItem item, User user, 
             bool includeChildren = true, 
             bool includeChildren = true, 
             bool includePeople = true)
             bool includePeople = true)
         {
         {
@@ -79,16 +78,16 @@ namespace MediaBrowser.Api
 
 
             dto.UserData = item.GetUserData(user);
             dto.UserData = item.GetUserData(user);
 
 
-            AttachStudios(dto, item);
+            await AttachStudios(dto, item);
 
 
             if (includeChildren)
             if (includeChildren)
             {
             {
-                AttachChildren(dto, item, user);
+                await AttachChildren(dto, item, user);
             }
             }
 
 
             if (includePeople)
             if (includePeople)
             {
             {
-                AttachPeople(dto, item);
+                await AttachPeople(dto, item);
             }
             }
 
 
             Folder folder = item as Folder;
             Folder folder = item as Folder;
@@ -104,18 +103,20 @@ namespace MediaBrowser.Api
             return dto;
             return dto;
         }
         }
 
 
-        private static void AttachStudios(DTOBaseItem dto, BaseItem item)
+        private static async Task AttachStudios(DTOBaseItem dto, BaseItem item)
         {
         {
             // Attach Studios by transforming them into BaseItemStudio (DTO)
             // Attach Studios by transforming them into BaseItemStudio (DTO)
             if (item.Studios != null)
             if (item.Studios != null)
             {
             {
+                IEnumerable<Studio> entities = await Task.WhenAll<Studio>(item.Studios.Select(c => Kernel.Instance.ItemController.GetStudio(c)));
+                
                 dto.Studios = item.Studios.Select(s =>
                 dto.Studios = item.Studios.Select(s =>
                 {
                 {
                     BaseItemStudio baseItemStudio = new BaseItemStudio();
                     BaseItemStudio baseItemStudio = new BaseItemStudio();
 
 
                     baseItemStudio.Name = s;
                     baseItemStudio.Name = s;
 
 
-                    Studio ibnObject = Kernel.Instance.ItemController.GetStudio(s);
+                    Studio ibnObject = entities.First(i => i.Name.Equals(s, StringComparison.OrdinalIgnoreCase));
 
 
                     if (ibnObject != null)
                     if (ibnObject != null)
                     {
                     {
@@ -127,30 +128,34 @@ namespace MediaBrowser.Api
             }
             }
         }
         }
 
 
-        private static void AttachChildren(DTOBaseItem dto, BaseItem item, User user)
+        private static async Task AttachChildren(DTOBaseItem dto, BaseItem item, User user)
         {
         {
             var folder = item as Folder;
             var folder = item as Folder;
 
 
             if (folder != null)
             if (folder != null)
             {
             {
-                dto.Children = folder.GetParentalAllowedChildren(user).Select(c => GetDTOBaseItem(c, user, false, false));
+                IEnumerable<BaseItem> children = folder.GetParentalAllowedChildren(user);
+
+                dto.Children = await Task.WhenAll<DTOBaseItem>(children.Select(c => GetDTOBaseItem(c, user, false, false)));
             }
             }
 
 
             dto.LocalTrailers = item.LocalTrailers;
             dto.LocalTrailers = item.LocalTrailers;
         }
         }
 
 
-        private static void AttachPeople(DTOBaseItem dto, BaseItem item)
+        private static async Task AttachPeople(DTOBaseItem dto, BaseItem item)
         {
         {
             // Attach People by transforming them into BaseItemPerson (DTO)
             // Attach People by transforming them into BaseItemPerson (DTO)
             if (item.People != null)
             if (item.People != null)
             {
             {
+                IEnumerable<Person> entities = await Task.WhenAll<Person>(item.People.Select(c => Kernel.Instance.ItemController.GetPerson(c.Name)));
+
                 dto.People = item.People.Select(p =>
                 dto.People = item.People.Select(p =>
                 {
                 {
                     BaseItemPerson baseItemPerson = new BaseItemPerson();
                     BaseItemPerson baseItemPerson = new BaseItemPerson();
 
 
                     baseItemPerson.PersonInfo = p;
                     baseItemPerson.PersonInfo = p;
 
 
-                    Person ibnObject = Kernel.Instance.ItemController.GetPerson(p.Name);
+                    Person ibnObject = entities.First(i => i.Name.Equals(p.Name, StringComparison.OrdinalIgnoreCase));
 
 
                     if (ibnObject != null)
                     if (ibnObject != null)
                     {
                     {

+ 5 - 9
MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs

@@ -5,7 +5,6 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Net;
 using System.Net;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Logging;
 using MediaBrowser.Common.Logging;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
@@ -91,20 +90,17 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        public override string ContentType
+        public override Task<string> GetContentType()
         {
         {
-            get
+            return Task.Run(() =>
             {
             {
                 return MimeTypes.GetMimeType("." + GetConversionOutputFormat());
                 return MimeTypes.GetMimeType("." + GetConversionOutputFormat());
-            }
+            });
         }
         }
 
 
-        public override bool CompressResponse
+        public override bool ShouldCompressResponse(string contentType)
         {
         {
-            get
-            {
-                return false;
-            }
+            return false;
         }
         }
 
 
         public override async Task ProcessRequest(HttpListenerContext ctx)
         public override async Task ProcessRequest(HttpListenerContext ctx)

+ 5 - 4
MediaBrowser.Api/HttpHandlers/GenreHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     /// </summary>
     public class GenreHandler : BaseJsonHandler<IBNItem<Genre>>
     public class GenreHandler : BaseJsonHandler<IBNItem<Genre>>
     {
     {
-        protected override IBNItem<Genre> GetObjectToSerialize()
+        protected override async Task<IBNItem<Genre>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
@@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
             string name = QueryString["name"];
             string name = QueryString["name"];
 
 
-            return GetGenre(parent, user, name);
+            return await GetGenre(parent, user, name);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Genre
         /// Gets a Genre
         /// </summary>
         /// </summary>
-        private IBNItem<Genre> GetGenre(Folder parent, User user, string name)
+        private async Task<IBNItem<Genre>> GetGenre(Folder parent, User user, string name)
         {
         {
             int count = 0;
             int count = 0;
 
 
@@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers
             // Get the original entity so that we can also supply the PrimaryImagePath
             // Get the original entity so that we can also supply the PrimaryImagePath
             return new IBNItem<Genre>()
             return new IBNItem<Genre>()
             {
             {
-                Item = Kernel.Instance.ItemController.GetGenre(name),
+                Item = await Kernel.Instance.ItemController.GetGenre(name),
                 BaseItemCount = count
                 BaseItemCount = count
             };
             };
         }
         }

+ 6 - 21
MediaBrowser.Api/HttpHandlers/GenresHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -10,20 +11,20 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class GenresHandler : BaseJsonHandler<IEnumerable<IBNItem<Genre>>>
     public class GenresHandler : BaseJsonHandler<IEnumerable<IBNItem<Genre>>>
     {
     {
-        protected override IEnumerable<IBNItem<Genre>> GetObjectToSerialize()
+        protected override async Task<IEnumerable<IBNItem<Genre>>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
 
 
-            return GetAllGenres(parent, user);
+            return await GetAllGenres(parent, user);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets all genres from all recursive children of a folder
         /// Gets all genres from all recursive children of a folder
         /// The CategoryInfo class is used to keep track of the number of times each genres appears
         /// The CategoryInfo class is used to keep track of the number of times each genres appears
         /// </summary>
         /// </summary>
-        private IEnumerable<IBNItem<Genre>> GetAllGenres(Folder parent, User user)
+        private async Task<IEnumerable<IBNItem<Genre>>> GetAllGenres(Folder parent, User user)
         {
         {
             Dictionary<string, int> data = new Dictionary<string, int>();
             Dictionary<string, int> data = new Dictionary<string, int>();
 
 
@@ -52,25 +53,9 @@ namespace MediaBrowser.Api.HttpHandlers
                 }
                 }
             }
             }
 
 
-            // Now go through the dictionary and create a Category for each genre
-            List<IBNItem<Genre>> list = new List<IBNItem<Genre>>();
+            IEnumerable<Genre> entities = await Task.WhenAll<Genre>(data.Keys.Select(key => { return Kernel.Instance.ItemController.GetGenre(key); }));
 
 
-            foreach (string key in data.Keys)
-            {
-                // Get the original entity so that we can also supply the PrimaryImagePath
-                Genre entity = Kernel.Instance.ItemController.GetGenre(key);
-
-                if (entity != null)
-                {
-                    list.Add(new IBNItem<Genre>()
-                    {
-                        Item = entity,
-                        BaseItemCount = data[key]
-                    });
-                }
-            }
-
-            return list;
+            return entities.Select(e => new IBNItem<Genre>() { Item = e, BaseItemCount = data[e.Name] });
         }
         }
     }
     }
 }
 }

+ 47 - 59
MediaBrowser.Api/HttpHandlers/ImageHandler.cs

@@ -13,39 +13,57 @@ namespace MediaBrowser.Api.HttpHandlers
     public class ImageHandler : BaseHandler
     public class ImageHandler : BaseHandler
     {
     {
         private string _ImagePath = null;
         private string _ImagePath = null;
-        private string ImagePath
+        private async Task<string> GetImagePath()
         {
         {
-            get
+            if (_ImagePath == null)
             {
             {
-                if (_ImagePath == null)
-                {
-                    _ImagePath = GetImagePath();
-                }
-
-                return _ImagePath;
+                _ImagePath = await DiscoverImagePath();
             }
             }
+
+            return _ImagePath;
         }
         }
 
 
-        private Stream _SourceStream = null;
-        private Stream SourceStream
+        private async Task<string> DiscoverImagePath()
         {
         {
-            get
+            string path = QueryString["path"] ?? string.Empty;
+
+            if (!string.IsNullOrEmpty(path))
             {
             {
-                EnsureSourceStream();
+                return path;
+            }
 
 
-                return _SourceStream;
+            string personName = QueryString["personname"];
+
+            if (!string.IsNullOrEmpty(personName))
+            {
+                Person person = await Kernel.Instance.ItemController.GetPerson(personName);
+                
+                return person.PrimaryImagePath;
             }
             }
+
+            BaseItem item = ApiService.GetItemById(QueryString["id"]);
+
+            string imageIndex = QueryString["index"];
+            int index = string.IsNullOrEmpty(imageIndex) ? 0 : int.Parse(imageIndex);
+
+            return GetImagePathFromTypes(item, ImageType, index);
         }
         }
 
 
+        private Stream _SourceStream = null;
+        private async Task<Stream> GetSourceStream()
+        {
+            await EnsureSourceStream();
+            return _SourceStream;
+        }
 
 
         private bool _SourceStreamEnsured = false;
         private bool _SourceStreamEnsured = false;
-        private void EnsureSourceStream()
+        private async Task EnsureSourceStream()
         {
         {
             if (!_SourceStreamEnsured)
             if (!_SourceStreamEnsured)
             {
             {
                 try
                 try
                 {
                 {
-                    _SourceStream = File.OpenRead(ImagePath);
+                    _SourceStream = File.OpenRead(await GetImagePath());
                 }
                 }
                 catch (FileNotFoundException ex)
                 catch (FileNotFoundException ex)
                 {
                 {
@@ -68,20 +86,17 @@ namespace MediaBrowser.Api.HttpHandlers
                 }
                 }
             }
             }
         }
         }
-        
-        public override string ContentType
+
+        public async override Task<string> GetContentType()
         {
         {
-            get
-            {
-                EnsureSourceStream();
+            await EnsureSourceStream();
 
 
-                if (SourceStream == null)
-                {
-                    return null;
-                }
-                
-                return MimeTypes.GetMimeType(ImagePath);
+            if (await GetSourceStream() == null)
+            {
+                return null;
             }
             }
+
+            return MimeTypes.GetMimeType(await GetImagePath());
         }
         }
 
 
         public override TimeSpan CacheDuration
         public override TimeSpan CacheDuration
@@ -92,16 +107,16 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        protected override DateTime? GetLastDateModified()
+        protected async override Task<DateTime?> GetLastDateModified()
         {
         {
-            EnsureSourceStream();
+            await EnsureSourceStream();
 
 
-            if (SourceStream == null)
+            if (await GetSourceStream() == null)
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            return File.GetLastWriteTime(ImagePath);
+            return File.GetLastWriteTime(await GetImagePath());
         }
         }
 
 
         private int? Height
         private int? Height
@@ -194,36 +209,9 @@ namespace MediaBrowser.Api.HttpHandlers
             }
             }
         }
         }
 
 
-        protected override Task WriteResponseToOutputStream(Stream stream)
+        protected override async Task WriteResponseToOutputStream(Stream stream)
         {
         {
-            return Task.Run(() =>
-            {
-                ImageProcessor.ProcessImage(SourceStream, stream, Width, Height, MaxWidth, MaxHeight, Quality);
-            });
-        }
-
-        private string GetImagePath()
-        {
-            string path = QueryString["path"] ?? string.Empty;
-
-            if (!string.IsNullOrEmpty(path))
-            {
-                return path;
-            }
-
-            string personName = QueryString["personname"];
-
-            if (!string.IsNullOrEmpty(personName))
-            {
-                return Kernel.Instance.ItemController.GetPerson(personName).PrimaryImagePath;
-            }
-
-            BaseItem item = ApiService.GetItemById(QueryString["id"]);
-
-            string imageIndex = QueryString["index"];
-            int index = string.IsNullOrEmpty(imageIndex) ? 0 : int.Parse(imageIndex);
-
-            return GetImagePathFromTypes(item, ImageType, index);
+            ImageProcessor.ProcessImage(await GetSourceStream(), stream, Width, Height, MaxWidth, MaxHeight, Quality);
         }
         }
 
 
         private string GetImagePathFromTypes(BaseItem item, ImageType imageType, int imageIndex)
         private string GetImagePathFromTypes(BaseItem item, ImageType imageType, int imageIndex)

+ 3 - 2
MediaBrowser.Api/HttpHandlers/ItemHandler.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -9,7 +10,7 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class ItemHandler : BaseJsonHandler<DTOBaseItem>
     public class ItemHandler : BaseJsonHandler<DTOBaseItem>
     {
     {
-        protected sealed override DTOBaseItem GetObjectToSerialize()
+        protected async override Task<DTOBaseItem> GetObjectToSerialize()
         {
         {
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
@@ -21,7 +22,7 @@ namespace MediaBrowser.Api.HttpHandlers
                 return null;
                 return null;
             }
             }
 
 
-            return ApiService.GetDTOBaseItem(item, user);
+            return await ApiService.GetDTOBaseItem(item, user);
         }
         }
 
 
         protected virtual BaseItem ItemToSerialize
         protected virtual BaseItem ItemToSerialize

+ 4 - 3
MediaBrowser.Api/HttpHandlers/ItemListHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -10,14 +11,14 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class ItemListHandler : BaseJsonHandler<IEnumerable<DTOBaseItem>>
     public class ItemListHandler : BaseJsonHandler<IEnumerable<DTOBaseItem>>
     {
     {
-        protected override IEnumerable<DTOBaseItem> GetObjectToSerialize()
+        protected override async Task<IEnumerable<DTOBaseItem>> GetObjectToSerialize()
         {
         {
             User user = Kernel.Instance.Users.First(u => u.Id == UserId);
             User user = Kernel.Instance.Users.First(u => u.Id == UserId);
 
 
-            return ItemsToSerialize.Select(i =>
+            return await Task.WhenAll<DTOBaseItem>(ItemsToSerialize.Select(i =>
             {
             {
                 return ApiService.GetDTOBaseItem(i, user, includeChildren: false, includePeople: false);
                 return ApiService.GetDTOBaseItem(i, user, includeChildren: false, includePeople: false);
-            });
+            }));
         }
         }
 
 
         protected IEnumerable<BaseItem> ItemsToSerialize
         protected IEnumerable<BaseItem> ItemsToSerialize

+ 5 - 4
MediaBrowser.Api/HttpHandlers/PersonHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     /// </summary>
     public class PersonHandler : BaseJsonHandler<IBNItem<Person>>
     public class PersonHandler : BaseJsonHandler<IBNItem<Person>>
     {
     {
-        protected override IBNItem<Person> GetObjectToSerialize()
+        protected async override Task<IBNItem<Person>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
@@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
             string name = QueryString["name"];
             string name = QueryString["name"];
 
 
-            return GetPerson(parent, user, name);
+            return await GetPerson(parent, user, name);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Person
         /// Gets a Person
         /// </summary>
         /// </summary>
-        private IBNItem<Person> GetPerson(Folder parent, User user, string name)
+        private async Task<IBNItem<Person>> GetPerson(Folder parent, User user, string name)
         {
         {
             int count = 0;
             int count = 0;
 
 
@@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers
             // Get the original entity so that we can also supply the PrimaryImagePath
             // Get the original entity so that we can also supply the PrimaryImagePath
             return new IBNItem<Person>()
             return new IBNItem<Person>()
             {
             {
-                Item = Kernel.Instance.ItemController.GetPerson(name),
+                Item = await Kernel.Instance.ItemController.GetPerson(name),
                 BaseItemCount = count
                 BaseItemCount = count
             };
             };
         }
         }

+ 7 - 3
MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Plugins;
 using MediaBrowser.Model.Plugins;
@@ -8,11 +9,14 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class PluginConfigurationHandler : BaseJsonHandler<BasePluginConfiguration>
     public class PluginConfigurationHandler : BaseJsonHandler<BasePluginConfiguration>
     {
     {
-        protected override BasePluginConfiguration GetObjectToSerialize()
+        protected override Task<BasePluginConfiguration> GetObjectToSerialize()
         {
         {
-            string pluginName = QueryString["name"];
+            return Task.Run(() =>
+            {
+                string pluginName = QueryString["name"];
 
 
-            return Kernel.Instance.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration;
+                return Kernel.Instance.Plugins.First(p => p.Name.Equals(pluginName, StringComparison.OrdinalIgnoreCase)).Configuration;
+            });
         }
         }
     }
     }
 }
 }

+ 19 - 15
MediaBrowser.Api/HttpHandlers/PluginsHandler.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -11,26 +12,29 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     /// </summary>
     public class PluginsHandler : BaseJsonHandler<IEnumerable<PluginInfo>>
     public class PluginsHandler : BaseJsonHandler<IEnumerable<PluginInfo>>
     {
     {
-        protected override IEnumerable<PluginInfo> GetObjectToSerialize()
+        protected override Task<IEnumerable<PluginInfo>> GetObjectToSerialize()
         {
         {
-            var plugins = Kernel.Instance.Plugins.Select(p =>
+            return Task.Run(() =>
             {
             {
-                return new PluginInfo()
+                var plugins = Kernel.Instance.Plugins.Select(p =>
                 {
                 {
-                    Path = p.Path,
-                    Name = p.Name,
-                    Enabled = p.Enabled,
-                    DownloadToUI = p.DownloadToUI,
-                    Version = p.Version
-                };
-            });
+                    return new PluginInfo()
+                    {
+                        Path = p.Path,
+                        Name = p.Name,
+                        Enabled = p.Enabled,
+                        DownloadToUI = p.DownloadToUI,
+                        Version = p.Version
+                    };
+                });
 
 
-            if (QueryString["uionly"] == "1")
-            {
-                plugins = plugins.Where(p => p.DownloadToUI);
-            }
+                if (QueryString["uionly"] == "1")
+                {
+                    plugins = plugins.Where(p => p.DownloadToUI);
+                }
 
 
-            return plugins;
+                return plugins;
+            });
         }
         }
     }
     }
 }
 }

+ 5 - 4
MediaBrowser.Api/HttpHandlers/StudioHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     /// </summary>
     public class StudioHandler : BaseJsonHandler<IBNItem<Studio>>
     public class StudioHandler : BaseJsonHandler<IBNItem<Studio>>
     {
     {
-        protected override IBNItem<Studio> GetObjectToSerialize()
+        protected async override Task<IBNItem<Studio>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
@@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
             string name = QueryString["name"];
             string name = QueryString["name"];
 
 
-            return GetStudio(parent, user, name);
+            return await GetStudio(parent, user, name);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Studio
         /// Gets a Studio
         /// </summary>
         /// </summary>
-        private IBNItem<Studio> GetStudio(Folder parent, User user, string name)
+        private async Task<IBNItem<Studio>> GetStudio(Folder parent, User user, string name)
         {
         {
             int count = 0;
             int count = 0;
 
 
@@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers
             // Get the original entity so that we can also supply the PrimaryImagePath
             // Get the original entity so that we can also supply the PrimaryImagePath
             return new IBNItem<Studio>()
             return new IBNItem<Studio>()
             {
             {
-                Item = Kernel.Instance.ItemController.GetStudio(name),
+                Item = await Kernel.Instance.ItemController.GetStudio(name),
                 BaseItemCount = count
                 BaseItemCount = count
             };
             };
         }
         }

+ 6 - 21
MediaBrowser.Api/HttpHandlers/StudiosHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -10,20 +11,20 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class StudiosHandler : BaseJsonHandler<IEnumerable<IBNItem<Studio>>>
     public class StudiosHandler : BaseJsonHandler<IEnumerable<IBNItem<Studio>>>
     {
     {
-        protected override IEnumerable<IBNItem<Studio>> GetObjectToSerialize()
+        protected override async Task<IEnumerable<IBNItem<Studio>>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
 
 
-            return GetAllStudios(parent, user);
+            return await GetAllStudios(parent, user);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets all studios from all recursive children of a folder
         /// Gets all studios from all recursive children of a folder
         /// The CategoryInfo class is used to keep track of the number of times each studio appears
         /// The CategoryInfo class is used to keep track of the number of times each studio appears
         /// </summary>
         /// </summary>
-        private IEnumerable<IBNItem<Studio>> GetAllStudios(Folder parent, User user)
+        private async Task<IEnumerable<IBNItem<Studio>>> GetAllStudios(Folder parent, User user)
         {
         {
             Dictionary<string, int> data = new Dictionary<string, int>();
             Dictionary<string, int> data = new Dictionary<string, int>();
 
 
@@ -52,25 +53,9 @@ namespace MediaBrowser.Api.HttpHandlers
                 }
                 }
             }
             }
 
 
-            // Now go through the dictionary and create a Category for each studio
-            List<IBNItem<Studio>> list = new List<IBNItem<Studio>>();
+            IEnumerable<Studio> entities = await Task.WhenAll<Studio>(data.Keys.Select(key => { return Kernel.Instance.ItemController.GetStudio(key); }));
 
 
-            foreach (string key in data.Keys)
-            {
-                // Get the original entity so that we can also supply the PrimaryImagePath
-                Studio entity = Kernel.Instance.ItemController.GetStudio(key);
-
-                if (entity != null)
-                {
-                    list.Add(new IBNItem<Studio>()
-                    {
-                        Item = entity,
-                        BaseItemCount = data[key]
-                    });
-                }
-            }
-
-            return list;
+            return entities.Select(e => new IBNItem<Studio>() { Item = e, BaseItemCount = data[e.Name] });
         }
         }
     }
     }
 }
 }

+ 6 - 2
MediaBrowser.Api/HttpHandlers/UsersHandler.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
@@ -7,9 +8,12 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     class UsersHandler : BaseJsonHandler<IEnumerable<User>>
     class UsersHandler : BaseJsonHandler<IEnumerable<User>>
     {
     {
-        protected override IEnumerable<User> GetObjectToSerialize()
+        protected override Task<IEnumerable<User>> GetObjectToSerialize()
         {
         {
-            return Kernel.Instance.Users;
+            return Task.Run(() =>
+            {
+                return Kernel.Instance.Users;
+            });
         }
         }
     }
     }
 }
 }

+ 5 - 4
MediaBrowser.Api/HttpHandlers/YearHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers
     /// </summary>
     /// </summary>
     public class YearHandler : BaseJsonHandler<IBNItem<Year>>
     public class YearHandler : BaseJsonHandler<IBNItem<Year>>
     {
     {
-        protected override IBNItem<Year> GetObjectToSerialize()
+        protected override async Task<IBNItem<Year>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
@@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers
 
 
             string year = QueryString["year"];
             string year = QueryString["year"];
 
 
-            return GetYear(parent, user, int.Parse(year));
+            return await GetYear(parent, user, int.Parse(year));
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Year
         /// Gets a Year
         /// </summary>
         /// </summary>
-        private IBNItem<Year> GetYear(Folder parent, User user, int year)
+        private async Task<IBNItem<Year>> GetYear(Folder parent, User user, int year)
         {
         {
             int count = 0;
             int count = 0;
 
 
@@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers
             // Get the original entity so that we can also supply the PrimaryImagePath
             // Get the original entity so that we can also supply the PrimaryImagePath
             return new IBNItem<Year>()
             return new IBNItem<Year>()
             {
             {
-                Item = Kernel.Instance.ItemController.GetYear(year),
+                Item = await Kernel.Instance.ItemController.GetYear(year),
                 BaseItemCount = count
                 BaseItemCount = count
             };
             };
         }
         }

+ 6 - 21
MediaBrowser.Api/HttpHandlers/YearsHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Common.Net.Handlers;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.DTO;
 using MediaBrowser.Model.DTO;
@@ -10,20 +11,20 @@ namespace MediaBrowser.Api.HttpHandlers
 {
 {
     public class YearsHandler : BaseJsonHandler<IEnumerable<IBNItem<Year>>>
     public class YearsHandler : BaseJsonHandler<IEnumerable<IBNItem<Year>>>
     {
     {
-        protected override IEnumerable<IBNItem<Year>> GetObjectToSerialize()
+        protected override async Task<IEnumerable<IBNItem<Year>>> GetObjectToSerialize()
         {
         {
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder;
             Guid userId = Guid.Parse(QueryString["userid"]);
             Guid userId = Guid.Parse(QueryString["userid"]);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
             User user = Kernel.Instance.Users.First(u => u.Id == userId);
 
 
-            return GetAllYears(parent, user);
+            return await GetAllYears(parent, user);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets all years from all recursive children of a folder
         /// Gets all years from all recursive children of a folder
         /// The CategoryInfo class is used to keep track of the number of times each year appears
         /// The CategoryInfo class is used to keep track of the number of times each year appears
         /// </summary>
         /// </summary>
-        private IEnumerable<IBNItem<Year>> GetAllYears(Folder parent, User user)
+        private async Task<IEnumerable<IBNItem<Year>>> GetAllYears(Folder parent, User user)
         {
         {
             Dictionary<int, int> data = new Dictionary<int, int>();
             Dictionary<int, int> data = new Dictionary<int, int>();
 
 
@@ -49,25 +50,9 @@ namespace MediaBrowser.Api.HttpHandlers
                 }
                 }
             }
             }
 
 
-            // Now go through the dictionary and create a Category for each studio
-            List<IBNItem<Year>> list = new List<IBNItem<Year>>();
+            IEnumerable<Year> entities = await Task.WhenAll<Year>(data.Keys.Select(key => { return Kernel.Instance.ItemController.GetYear(key); }));
 
 
-            foreach (int key in data.Keys)
-            {
-                // Get the original entity so that we can also supply the PrimaryImagePath
-                Year entity = Kernel.Instance.ItemController.GetYear(key);
-
-                if (entity != null)
-                {
-                    list.Add(new IBNItem<Year>()
-                    {
-                        Item = entity,
-                        BaseItemCount = data[key]
-                    });
-                }
-            }
-
-            return list;
+            return entities.Select(e => new IBNItem<Year>() { Item = e, BaseItemCount = data[int.Parse(e.Name)] });
         }
         }
     }
     }
 }
 }

+ 26 - 2
MediaBrowser.Common/Kernel/BaseKernel.cs

@@ -2,17 +2,16 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel.Composition;
 using System.ComponentModel.Composition;
 using System.ComponentModel.Composition.Hosting;
 using System.ComponentModel.Composition.Hosting;
-using System.Configuration;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
+using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Logging;
 using MediaBrowser.Common.Logging;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Plugins;
 using MediaBrowser.Common.Serialization;
 using MediaBrowser.Common.Serialization;
 using MediaBrowser.Model.Progress;
 using MediaBrowser.Model.Progress;
-using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Common.Kernel
 namespace MediaBrowser.Common.Kernel
 {
 {
@@ -93,6 +92,8 @@ namespace MediaBrowser.Common.Kernel
         /// </summary>
         /// </summary>
         protected void ReloadComposableParts()
         protected void ReloadComposableParts()
         {
         {
+            DisposeComposableParts();
+            
             // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
             // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
             // This will prevent the .dll file from getting locked, and allow us to replace it when needed
             // This will prevent the .dll file from getting locked, and allow us to replace it when needed
             IEnumerable<Assembly> pluginAssemblies = Directory.GetFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories).Select(f => Assembly.Load(File.ReadAllBytes((f))));
             IEnumerable<Assembly> pluginAssemblies = Directory.GetFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories).Select(f => Assembly.Load(File.ReadAllBytes((f))));
@@ -203,10 +204,33 @@ namespace MediaBrowser.Common.Kernel
         /// </summary>
         /// </summary>
         public virtual void Dispose()
         public virtual void Dispose()
         {
         {
+            DisposeComposableParts();
             DisposeHttpServer();
             DisposeHttpServer();
             DisposeLogger();
             DisposeLogger();
         }
         }
 
 
+        /// <summary>
+        /// Disposes all objects gathered through MEF composable parts
+        /// </summary>
+        protected virtual void DisposeComposableParts()
+        {
+            DisposePlugins();
+        }
+
+        /// <summary>
+        /// Disposes all plugins
+        /// </summary>
+        private void DisposePlugins()
+        {
+            if (Plugins != null)
+            {
+                foreach (BasePlugin plugin in Plugins)
+                {
+                    plugin.Dispose();
+                }
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Disposes the current HttpServer
         /// Disposes the current HttpServer
         /// </summary>
         /// </summary>

+ 3 - 3
MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs

@@ -14,9 +14,9 @@ namespace MediaBrowser.Common.Net.Handlers
 
 
         protected string ResourcePath { get; set; }
         protected string ResourcePath { get; set; }
 
 
-        public override string ContentType
+        public override Task<string> GetContentType()
         {
         {
-            get
+            return Task.Run(() =>
             {
             {
                 string extension = Path.GetExtension(ResourcePath);
                 string extension = Path.GetExtension(ResourcePath);
 
 
@@ -46,7 +46,7 @@ namespace MediaBrowser.Common.Net.Handlers
                 }
                 }
 
 
                 return "text/plain; charset=utf-8";
                 return "text/plain; charset=utf-8";
-            }
+            });
         }
         }
 
 
         protected override Task WriteResponseToOutputStream(Stream stream)
         protected override Task WriteResponseToOutputStream(Stream stream)

+ 23 - 38
MediaBrowser.Common/Net/Handlers/BaseHandler.cs

@@ -111,7 +111,7 @@ namespace MediaBrowser.Common.Net.Handlers
         /// <summary>
         /// <summary>
         /// Gets the MIME type to include in the response headers
         /// Gets the MIME type to include in the response headers
         /// </summary>
         /// </summary>
-        public abstract string ContentType { get; }
+        public abstract Task<string> GetContentType();
 
 
         /// <summary>
         /// <summary>
         /// Gets the status code to include in the response headers
         /// Gets the status code to include in the response headers
@@ -129,31 +129,9 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
-        private bool _LastDateModifiedDiscovered = false;
-        private DateTime? _LastDateModified = null;
-        /// <summary>
-        /// Gets the last date modified of the content being returned, if this can be determined.
-        /// This will be used to invalidate the cache, so it's not needed if CacheDuration is 0.
-        /// </summary>
-        public DateTime? LastDateModified
+        public virtual bool ShouldCompressResponse(string contentType)
         {
         {
-            get
-            {
-                if (!_LastDateModifiedDiscovered)
-                {
-                    _LastDateModified = GetLastDateModified();
-                }
-
-                return _LastDateModified;
-            }
-        }
-        
-        public virtual bool CompressResponse
-        {
-            get
-            {
-                return true;
-            }
+            return true;
         }
         }
 
 
         private bool ClientSupportsCompression
         private bool ClientSupportsCompression
@@ -207,10 +185,12 @@ namespace MediaBrowser.Common.Net.Handlers
                 // When serving a range request, we need to return status code 206 to indicate a partial response body
                 // When serving a range request, we need to return status code 206 to indicate a partial response body
                 StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200;
                 StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200;
 
 
-                ctx.Response.ContentType = ContentType;
+                ctx.Response.ContentType = await GetContentType();
 
 
                 TimeSpan cacheDuration = CacheDuration;
                 TimeSpan cacheDuration = CacheDuration;
 
 
+                DateTime? lastDateModified = await GetLastDateModified();
+
                 if (ctx.Request.Headers.AllKeys.Contains("If-Modified-Since"))
                 if (ctx.Request.Headers.AllKeys.Contains("If-Modified-Since"))
                 {
                 {
                     DateTime ifModifiedSince;
                     DateTime ifModifiedSince;
@@ -218,18 +198,20 @@ namespace MediaBrowser.Common.Net.Handlers
                     if (DateTime.TryParse(ctx.Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince))
                     if (DateTime.TryParse(ctx.Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince))
                     {
                     {
                         // If the cache hasn't expired yet just return a 304
                         // If the cache hasn't expired yet just return a 304
-                        if (IsCacheValid(ifModifiedSince, cacheDuration, LastDateModified))
+                        if (IsCacheValid(ifModifiedSince, cacheDuration, lastDateModified))
                         {
                         {
                             StatusCode = 304;
                             StatusCode = 304;
                         }
                         }
                     }
                     }
                 }
                 }
 
 
-                PrepareResponse();
+                await PrepareResponse();
 
 
                 if (IsResponseValid)
                 if (IsResponseValid)
                 {
                 {
-                    await ProcessUncachedRequest(ctx, cacheDuration);
+                    bool compressResponse = ShouldCompressResponse(ctx.Response.ContentType) && ClientSupportsCompression;
+
+                    await ProcessUncachedRequest(ctx, compressResponse, cacheDuration, lastDateModified);
                 }
                 }
                 else
                 else
                 {
                 {
@@ -241,7 +223,7 @@ namespace MediaBrowser.Common.Net.Handlers
             {
             {
                 // It might be too late if some response data has already been transmitted, but try to set this
                 // It might be too late if some response data has already been transmitted, but try to set this
                 ctx.Response.StatusCode = 500;
                 ctx.Response.StatusCode = 500;
-                
+
                 Logger.LogException(ex);
                 Logger.LogException(ex);
             }
             }
             finally
             finally
@@ -250,7 +232,7 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
-        private async Task ProcessUncachedRequest(HttpListenerContext ctx, TimeSpan cacheDuration)
+        private async Task ProcessUncachedRequest(HttpListenerContext ctx, bool compressResponse, TimeSpan cacheDuration, DateTime? lastDateModified)
         {
         {
             long? totalContentLength = TotalContentLength;
             long? totalContentLength = TotalContentLength;
 
 
@@ -270,7 +252,7 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
 
 
             // Add the compression header
             // Add the compression header
-            if (CompressResponse && ClientSupportsCompression)
+            if (compressResponse)
             {
             {
                 ctx.Response.AddHeader("Content-Encoding", CompressionMethod);
                 ctx.Response.AddHeader("Content-Encoding", CompressionMethod);
             }
             }
@@ -278,7 +260,7 @@ namespace MediaBrowser.Common.Net.Handlers
             // Add caching headers
             // Add caching headers
             if (cacheDuration.Ticks > 0)
             if (cacheDuration.Ticks > 0)
             {
             {
-                CacheResponse(ctx.Response, cacheDuration, LastDateModified);
+                CacheResponse(ctx.Response, cacheDuration, lastDateModified);
             }
             }
 
 
             // Set the status code
             // Set the status code
@@ -289,7 +271,7 @@ namespace MediaBrowser.Common.Net.Handlers
                 // Finally, write the response data
                 // Finally, write the response data
                 Stream outputStream = ctx.Response.OutputStream;
                 Stream outputStream = ctx.Response.OutputStream;
 
 
-                if (CompressResponse && ClientSupportsCompression)
+                if (compressResponse)
                 {
                 {
                     if (CompressionMethod.Equals("deflate", StringComparison.OrdinalIgnoreCase))
                     if (CompressionMethod.Equals("deflate", StringComparison.OrdinalIgnoreCase))
                     {
                     {
@@ -321,10 +303,11 @@ namespace MediaBrowser.Common.Net.Handlers
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Gives subclasses a chance to do and prep work, and also to validate data and set an error status code, if needed
+        /// Gives subclasses a chance to do any prep work, and also to validate data and set an error status code, if needed
         /// </summary>
         /// </summary>
-        protected virtual void PrepareResponse()
+        protected virtual Task PrepareResponse()
         {
         {
+            return Task.Run(() => { });
         }
         }
 
 
         protected abstract Task WriteResponseToOutputStream(Stream stream);
         protected abstract Task WriteResponseToOutputStream(Stream stream);
@@ -372,9 +355,11 @@ namespace MediaBrowser.Common.Net.Handlers
             return null;
             return null;
         }
         }
 
 
-        protected virtual DateTime? GetLastDateModified()
+        protected virtual Task<DateTime?> GetLastDateModified()
         {
         {
-            return null;
+            DateTime? value = null;
+
+            return Task.Run<DateTime?>(() => { return value; });
         }
         }
 
 
         private bool IsResponseValid
         private bool IsResponseValid

+ 14 - 23
MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs

@@ -6,19 +6,22 @@ namespace MediaBrowser.Common.Net.Handlers
 {
 {
     public abstract class BaseJsonHandler<T> : BaseHandler
     public abstract class BaseJsonHandler<T> : BaseHandler
     {
     {
-        public override string ContentType
+        public override Task<string> GetContentType()
         {
         {
-            get { return MimeTypes.JsonMimeType; }
+            return Task.Run(() =>
+            {
+                return MimeTypes.JsonMimeType;
+            });
         }
         }
 
 
         private bool _ObjectToSerializeEnsured = false;
         private bool _ObjectToSerializeEnsured = false;
         private T _ObjectToSerialize;
         private T _ObjectToSerialize;
      
      
-        private void EnsureObjectToSerialize()
+        private async Task EnsureObjectToSerialize()
         {
         {
             if (!_ObjectToSerializeEnsured)
             if (!_ObjectToSerializeEnsured)
             {
             {
-                _ObjectToSerialize = GetObjectToSerialize();
+                _ObjectToSerialize = await GetObjectToSerialize();
 
 
                 if (_ObjectToSerialize == null)
                 if (_ObjectToSerialize == null)
                 {
                 {
@@ -29,30 +32,18 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
-        private T ObjectToSerialize
-        {
-            get
-            {
-                EnsureObjectToSerialize();
-                return _ObjectToSerialize;
-            }
-        }
+        protected abstract Task<T> GetObjectToSerialize();
 
 
-        protected abstract T GetObjectToSerialize();
-
-        protected override void PrepareResponse()
+        protected override async Task PrepareResponse()
         {
         {
-            base.PrepareResponse();
-
-            EnsureObjectToSerialize();
+            await EnsureObjectToSerialize();
         }
         }
 
 
-        protected override Task WriteResponseToOutputStream(Stream stream)
+        protected async override Task WriteResponseToOutputStream(Stream stream)
         {
         {
-            return Task.Run(() =>
-            {
-                JsonSerializer.SerializeToStream<T>(ObjectToSerialize, stream);
-            });
+            await EnsureObjectToSerialize();
+
+            JsonSerializer.SerializeToStream<T>(_ObjectToSerialize, stream);
         }
         }
     }
     }
 }
 }

+ 26 - 30
MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs

@@ -77,27 +77,22 @@ namespace MediaBrowser.Common.Net.Handlers
             }
             }
         }
         }
 
 
-        public override bool CompressResponse
+        public override bool ShouldCompressResponse(string contentType)
         {
         {
-            get
+            // Can't compress these
+            if (IsRangeRequest)
             {
             {
-                // Can't compress these
-                if (IsRangeRequest)
-                {
-                    return false;
-                }
-
-                string contentType = ContentType;
-
-                // Don't compress media
-                if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
-                {
-                    return false;
-                }
+                return false;
+            }
 
 
-                // It will take some work to support compression within this handler
+            // Don't compress media
+            if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
+            {
                 return false;
                 return false;
             }
             }
+
+            // It will take some work to support compression within this handler
+            return false;
         }
         }
 
 
         protected override long? GetTotalContentLength()
         protected override long? GetTotalContentLength()
@@ -105,31 +100,32 @@ namespace MediaBrowser.Common.Net.Handlers
             return SourceStream.Length;
             return SourceStream.Length;
         }
         }
 
 
-        protected override DateTime? GetLastDateModified()
+        protected override Task<DateTime?> GetLastDateModified()
         {
         {
-            EnsureSourceStream();
-
-            if (SourceStream == null)
+            return Task.Run<DateTime?>(() =>
             {
             {
-                return null;
-            }
+                EnsureSourceStream();
+
+                if (SourceStream == null)
+                {
+                    return null;
+                }
 
 
-            return File.GetLastWriteTime(Path);
+                return File.GetLastWriteTime(Path);
+            });
         }
         }
 
 
-        public override string ContentType
+        public override Task<string> GetContentType()
         {
         {
-            get
+            return Task.Run(() =>
             {
             {
                 return MimeTypes.GetMimeType(Path);
                 return MimeTypes.GetMimeType(Path);
-            }
+            });
         }
         }
 
 
-        protected override void PrepareResponse()
+        protected override Task PrepareResponse()
         {
         {
-            base.PrepareResponse();
-
-            EnsureSourceStream();
+            return Task.Run(() => { EnsureSourceStream(); });
         }
         }
 
 
         protected async override Task WriteResponseToOutputStream(Stream stream)
         protected async override Task WriteResponseToOutputStream(Stream stream)

+ 3 - 6
MediaBrowser.Controller/IO/DirectoryWatchers.cs

@@ -75,7 +75,7 @@ namespace MediaBrowser.Controller.IO
             }
             }
         }
         }
 
 
-        private void TimerStopped(object stateInfo)
+        private async void TimerStopped(object stateInfo)
         {
         {
             updateTimer.Dispose();
             updateTimer.Dispose();
             updateTimer = null;
             updateTimer = null;
@@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.IO
             List<string> paths = affectedPaths;
             List<string> paths = affectedPaths;
             affectedPaths = new List<string>();
             affectedPaths = new List<string>();
 
 
-            //ProcessPathChanges(paths);
+            await ProcessPathChanges(paths);
         }
         }
 
 
         private async Task ProcessPathChanges(IEnumerable<string> paths)
         private async Task ProcessPathChanges(IEnumerable<string> paths)
@@ -109,10 +109,7 @@ namespace MediaBrowser.Controller.IO
             }
             }
             else
             else
             {
             {
-                /*Parallel.For(0, itemsToRefresh.Count, i =>
-                {
-                    Kernel.Instance.ReloadItem(itemsToRefresh[i]);
-                });*/
+                await Task.WhenAll(itemsToRefresh.Select(i => Kernel.Instance.ReloadItem(i)));
             }
             }
         }
         }
 
 

+ 42 - 0
MediaBrowser.Controller/Kernel.cs

@@ -248,5 +248,47 @@ namespace MediaBrowser.Controller
 
 
             return list;
             return list;
         }
         }
+
+        internal async Task ExecuteMetadataProviders(BaseEntity item, ItemResolveEventArgs args)
+        {
+            var supportedProviders = Kernel.Instance.MetadataProviders.Where(i => i.Supports(item));
+
+            // Start with non-internet providers. Run them sequentially
+            foreach (BaseMetadataProvider provider in supportedProviders.Where(i => !i.RequiresInternet))
+            {
+                await provider.Fetch(item, args);
+            }
+
+            var internetProviders = supportedProviders.Where(i => i.RequiresInternet);
+
+            if (internetProviders.Any())
+            {
+                // Now execute internet providers in parallel
+                await Task.WhenAll(
+                    internetProviders.Select(i => i.Fetch(item, args))
+                    );
+            }
+        }
+
+        protected override void DisposeComposableParts()
+        {
+            base.DisposeComposableParts();
+
+            DisposeProviders();
+        }
+
+        /// <summary>
+        /// Disposes all providers
+        /// </summary>
+        private void DisposeProviders()
+        {
+            if (MetadataProviders != null)
+            {
+                foreach (var provider in MetadataProviders)
+                {
+                    provider.Dispose();
+                }
+            }
+        }
     }
     }
 }
 }

+ 33 - 27
MediaBrowser.Controller/Library/ItemController.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
@@ -217,49 +218,49 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// <summary>
         /// Gets a Person
         /// Gets a Person
         /// </summary>
         /// </summary>
-        public Person GetPerson(string name)
+        public async Task<Person> GetPerson(string name)
         {
         {
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.PeoplePath, name);
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.PeoplePath, name);
 
 
-            return GetImagesByNameItem<Person>(path, name);
+            return await GetImagesByNameItem<Person>(path, name);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Studio
         /// Gets a Studio
         /// </summary>
         /// </summary>
-        public Studio GetStudio(string name)
+        public async Task<Studio> GetStudio(string name)
         {
         {
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.StudioPath, name);
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.StudioPath, name);
 
 
-            return GetImagesByNameItem<Studio>(path, name);
+            return await GetImagesByNameItem<Studio>(path, name);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Genre
         /// Gets a Genre
         /// </summary>
         /// </summary>
-        public Genre GetGenre(string name)
+        public async Task<Genre> GetGenre(string name)
         {
         {
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.GenrePath, name);
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.GenrePath, name);
 
 
-            return GetImagesByNameItem<Genre>(path, name);
+            return await GetImagesByNameItem<Genre>(path, name);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets a Year
         /// Gets a Year
         /// </summary>
         /// </summary>
-        public Year GetYear(int value)
+        public async Task<Year> GetYear(int value)
         {
         {
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.YearPath, value.ToString());
             string path = Path.Combine(Kernel.Instance.ApplicationPaths.YearPath, value.ToString());
 
 
-            return GetImagesByNameItem<Year>(path, value.ToString());
+            return await GetImagesByNameItem<Year>(path, value.ToString());
         }
         }
 
 
-        private Dictionary<string, object> ImagesByNameItemCache = new Dictionary<string, object>();
+        private ConcurrentDictionary<string, object> ImagesByNameItemCache = new ConcurrentDictionary<string, object>();
 
 
         /// <summary>
         /// <summary>
         /// Generically retrieves an IBN item
         /// Generically retrieves an IBN item
         /// </summary>
         /// </summary>
-        private T GetImagesByNameItem<T>(string path, string name)
+        private async Task<T> GetImagesByNameItem<T>(string path, string name)
             where T : BaseEntity, new()
             where T : BaseEntity, new()
         {
         {
             string key = path.ToLower();
             string key = path.ToLower();
@@ -267,7 +268,9 @@ namespace MediaBrowser.Controller.Library
             // Look for it in the cache, if it's not there, create it
             // Look for it in the cache, if it's not there, create it
             if (!ImagesByNameItemCache.ContainsKey(key))
             if (!ImagesByNameItemCache.ContainsKey(key))
             {
             {
-                ImagesByNameItemCache[key] = CreateImagesByNameItem<T>(path, name);
+                T obj = await CreateImagesByNameItem<T>(path, name);
+                ImagesByNameItemCache[key] = obj;
+                return obj;
             }
             }
 
 
             return ImagesByNameItemCache[key] as T;
             return ImagesByNameItemCache[key] as T;
@@ -276,7 +279,7 @@ namespace MediaBrowser.Controller.Library
         /// <summary>
         /// <summary>
         /// Creates an IBN item based on a given path
         /// Creates an IBN item based on a given path
         /// </summary>
         /// </summary>
-        private T CreateImagesByNameItem<T>(string path, string name)
+        private async Task<T> CreateImagesByNameItem<T>(string path, string name)
             where T : BaseEntity, new()
             where T : BaseEntity, new()
         {
         {
             T item = new T();
             T item = new T();
@@ -284,25 +287,28 @@ namespace MediaBrowser.Controller.Library
             item.Name = name;
             item.Name = name;
             item.Id = Kernel.GetMD5(path);
             item.Id = Kernel.GetMD5(path);
 
 
-            if (Directory.Exists(path))
+            if (!Directory.Exists(path))
             {
             {
-                item.DateCreated = Directory.GetCreationTime(path);
-                item.DateModified = Directory.GetLastAccessTime(path);
-                if (File.Exists(Path.Combine(path, "folder.jpg")))
-                {
-                    item.PrimaryImagePath = Path.Combine(path, "folder.jpg");
-                }
-                else if (File.Exists(Path.Combine(path, "folder.png")))
-                {
-                    item.PrimaryImagePath = Path.Combine(path, "folder.png");
-                }
+                Directory.CreateDirectory(path);
             }
             }
-            else
+
+            item.DateCreated = Directory.GetCreationTime(path);
+            item.DateModified = Directory.GetLastAccessTime(path);
+
+            if (File.Exists(Path.Combine(path, "folder.jpg")))
+            {
+                item.PrimaryImagePath = Path.Combine(path, "folder.jpg");
+            }
+            else if (File.Exists(Path.Combine(path, "folder.png")))
             {
             {
-                DateTime now = DateTime.Now;
+                item.PrimaryImagePath = Path.Combine(path, "folder.png");
+            }
+
+            var b = false;
 
 
-                item.DateCreated = now;
-                item.DateModified = now;
+            if (b)
+            {
+                await Kernel.Instance.ExecuteMetadataProviders(item, null);
             }
             }
 
 
             return item;
             return item;

+ 3 - 2
MediaBrowser.Controller/Providers/AudioInfoProvider.cs

@@ -12,12 +12,12 @@ namespace MediaBrowser.Controller.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class AudioInfoProvider : BaseMetadataProvider
     public class AudioInfoProvider : BaseMetadataProvider
     {
     {
-        public override bool Supports(BaseItem item)
+        public override bool Supports(BaseEntity item)
         {
         {
             return item is Audio;
             return item is Audio;
         }
         }
 
 
-        public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             Audio audio = item as Audio;
             Audio audio = item as Audio;
 
 
@@ -62,6 +62,7 @@ namespace MediaBrowser.Controller.Providers
         {
         {
             base.Init();
             base.Init();
 
 
+            // Do this now so that we don't have to do this on every operation, which would require us to create a lock in order to maintain thread-safety
             for (int i = 0; i <= 9; i++)
             for (int i = 0; i <= 9; i++)
             {
             {
                 EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, i.ToString()));
                 EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, i.ToString()));

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

@@ -1,10 +1,11 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Controller.Providers
 namespace MediaBrowser.Controller.Providers
 {
 {
-    public abstract class BaseMetadataProvider
+    public abstract class BaseMetadataProvider : IDisposable
     {
     {
         /// <summary>
         /// <summary>
         /// If the provider needs any startup routines, add them here
         /// If the provider needs any startup routines, add them here
@@ -13,11 +14,23 @@ namespace MediaBrowser.Controller.Providers
         {
         {
         }
         }
 
 
-        public virtual bool Supports(BaseItem item)
+        /// <summary>
+        /// Disposes anything created during Init
+        /// </summary>
+        public virtual void Dispose()
+        {
+        }
+
+        public abstract bool Supports(BaseEntity item);
+
+        public virtual bool RequiresInternet
         {
         {
-            return true;
+            get
+            {
+                return false;
+            }
         }
         }
 
 
-        public abstract Task Fetch(BaseItem item, ItemResolveEventArgs args);
+        public abstract Task Fetch(BaseEntity item, ItemResolveEventArgs args);
     }
     }
 }
 }

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

@@ -9,12 +9,12 @@ namespace MediaBrowser.Controller.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class FolderProviderFromXml : BaseMetadataProvider
     public class FolderProviderFromXml : BaseMetadataProvider
     {
     {
-        public override bool Supports(BaseItem item)
+        public override bool Supports(BaseEntity item)
         {
         {
             return item is Folder;
             return item is Folder;
         }
         }
 
 
-        public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             var metadataFile = args.GetFileByName("folder.xml");
             var metadataFile = args.GetFileByName("folder.xml");
 
 

+ 7 - 2
MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs

@@ -12,13 +12,18 @@ namespace MediaBrowser.Controller.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class ImageFromMediaLocationProvider : BaseMetadataProvider
     public class ImageFromMediaLocationProvider : BaseMetadataProvider
     {
     {
-        public override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public override bool Supports(BaseEntity item)
+        {
+            return item is BaseItem;
+        }
+        
+        public override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             return Task.Run(() =>
             return Task.Run(() =>
             {
             {
                 if (args.IsFolder)
                 if (args.IsFolder)
                 {
                 {
-                    PopulateImages(item, args);
+                    PopulateImages(item as BaseItem, args);
                 }
                 }
             });
             });
         }
         }

+ 9 - 2
MediaBrowser.Controller/Providers/LocalTrailerProvider.cs

@@ -10,8 +10,15 @@ namespace MediaBrowser.Controller.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class LocalTrailerProvider : BaseMetadataProvider
     public class LocalTrailerProvider : BaseMetadataProvider
     {
     {
-        public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public override bool Supports(BaseEntity item)
         {
         {
+            return item is BaseItem;
+        }
+
+        public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
+        {
+            BaseItem baseItem = item as BaseItem;
+
             var trailerPath = args.GetFolderByName("trailers");
             var trailerPath = args.GetFolderByName("trailers");
 
 
             if (trailerPath.HasValue)
             if (trailerPath.HasValue)
@@ -32,7 +39,7 @@ namespace MediaBrowser.Controller.Providers
                     }
                     }
                 }
                 }
 
 
-                item.LocalTrailers = localTrailers;
+                baseItem.LocalTrailers = localTrailers;
             }
             }
         }
         }
     }
     }

+ 6 - 18
MediaBrowser.Controller/Resolvers/BaseItemResolver.cs

@@ -2,13 +2,12 @@
 using System.IO;
 using System.IO;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Events;
 using MediaBrowser.Controller.Events;
-using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Controller.Resolvers
 namespace MediaBrowser.Controller.Resolvers
 {
 {
     public abstract class BaseItemResolver<T> : IBaseItemResolver
     public abstract class BaseItemResolver<T> : IBaseItemResolver
-        where T : BaseItem, new ()
+        where T : BaseItem, new()
     {
     {
         protected virtual T Resolve(ItemResolveEventArgs args)
         protected virtual T Resolve(ItemResolveEventArgs args)
         {
         {
@@ -18,7 +17,7 @@ namespace MediaBrowser.Controller.Resolvers
         /// <summary>
         /// <summary>
         /// Sets initial values on the newly resolved item
         /// Sets initial values on the newly resolved item
         /// </summary>
         /// </summary>
-        protected virtual void SetItemValues(T item, ItemResolveEventArgs args)
+        protected virtual void SetInitialItemValues(T item, ItemResolveEventArgs args)
         {
         {
             // If the subclass didn't specify this
             // If the subclass didn't specify this
             if (string.IsNullOrEmpty(item.Path))
             if (string.IsNullOrEmpty(item.Path))
@@ -38,35 +37,24 @@ namespace MediaBrowser.Controller.Resolvers
         public async Task<BaseItem> ResolvePath(ItemResolveEventArgs args)
         public async Task<BaseItem> ResolvePath(ItemResolveEventArgs args)
         {
         {
             T item = Resolve(args);
             T item = Resolve(args);
-            
+
             if (item != null)
             if (item != null)
             {
             {
                 // Set initial values on the newly resolved item
                 // Set initial values on the newly resolved item
-                SetItemValues(item, args);
+                SetInitialItemValues(item, args);
 
 
                 // Make sure the item has a name
                 // Make sure the item has a name
                 EnsureName(item);
                 EnsureName(item);
 
 
                 // Make sure DateCreated and DateModified have values
                 // Make sure DateCreated and DateModified have values
                 EnsureDates(item);
                 EnsureDates(item);
-                
-                await FetchMetadataFromProviders(item, args);
+
+                await Kernel.Instance.ExecuteMetadataProviders(item, args);
             }
             }
 
 
             return item;
             return item;
         }
         }
 
 
-        private async Task FetchMetadataFromProviders(T item, ItemResolveEventArgs args)
-        {
-            foreach (BaseMetadataProvider provider in Kernel.Instance.MetadataProviders)
-            {
-                if (provider.Supports(item))
-                {
-                    await provider.Fetch(item, args);
-                }
-            }
-        }
-
         private void EnsureName(T item)
         private void EnsureName(T item)
         {
         {
             // If the subclass didn't supply a name, add it here
             // If the subclass didn't supply a name, add it here

+ 2 - 2
MediaBrowser.Controller/Resolvers/FolderResolver.cs

@@ -21,9 +21,9 @@ namespace MediaBrowser.Controller.Resolvers
     public abstract class BaseFolderResolver<TItemType> : BaseItemResolver<TItemType>
     public abstract class BaseFolderResolver<TItemType> : BaseItemResolver<TItemType>
         where TItemType : Folder, new()
         where TItemType : Folder, new()
     {
     {
-        protected override void SetItemValues(TItemType item, ItemResolveEventArgs args)
+        protected override void SetInitialItemValues(TItemType item, ItemResolveEventArgs args)
         {
         {
-            base.SetItemValues(item, args);
+            base.SetInitialItemValues(item, args);
 
 
             item.IsRoot = args.Parent == null;
             item.IsRoot = args.Parent == null;
         }
         }

+ 2 - 2
MediaBrowser.Controller/Resolvers/VirtualFolderResolver.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Resolvers
             return null;
             return null;
         }
         }
 
 
-        protected override void SetItemValues(VirtualFolder item, ItemResolveEventArgs args)
+        protected override void SetInitialItemValues(VirtualFolder item, ItemResolveEventArgs args)
         {
         {
             // Set the name initially by stripping off the [CollectionType=...]
             // Set the name initially by stripping off the [CollectionType=...]
             // The name can always be overridden later by folder.xml
             // The name can always be overridden later by folder.xml
@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Resolvers
                 item.CollectionType = pathName.Substring(index + srch.Length).TrimEnd(']');
                 item.CollectionType = pathName.Substring(index + srch.Length).TrimEnd(']');
             }
             }
 
 
-            base.SetItemValues(item, args);
+            base.SetInitialItemValues(item, args);
         }
         }
 
 
     }
     }

+ 1 - 1
MediaBrowser.Controller/Xml/BaseItemXmlParser.cs

@@ -2,9 +2,9 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Threading.Tasks;
 using System.Xml;
 using System.Xml;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
-using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Controller.Xml
 namespace MediaBrowser.Controller.Xml
 {
 {

+ 2 - 2
MediaBrowser.Movies/Providers/MovieProviderFromXml.cs

@@ -11,12 +11,12 @@ namespace MediaBrowser.Movies.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class MovieProviderFromXml : BaseMetadataProvider
     public class MovieProviderFromXml : BaseMetadataProvider
     {
     {
-        public override bool Supports(BaseItem item)
+        public override bool Supports(BaseEntity item)
         {
         {
             return item is Movie;
             return item is Movie;
         }
         }
 
 
-        public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             var metadataFile = args.GetFileByName("movie.xml");
             var metadataFile = args.GetFileByName("movie.xml");
 
 

+ 2 - 2
MediaBrowser.Movies/Resolvers/MovieResolver.cs

@@ -88,9 +88,9 @@ namespace MediaBrowser.Movies.Resolvers
             }
             }
         }
         }
 
 
-        protected override void SetItemValues(Movie item, ItemResolveEventArgs args)
+        protected override void SetInitialItemValues(Movie item, ItemResolveEventArgs args)
         {
         {
-            base.SetItemValues(item, args);
+            base.SetInitialItemValues(item, args);
 
 
             PopulateBonusFeatures(item, args);
             PopulateBonusFeatures(item, args);
         }
         }

+ 1 - 2
MediaBrowser.ServerApplication/MainWindow.xaml.cs

@@ -2,10 +2,9 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Windows;
 using System.Windows;
 using MediaBrowser.Common.Logging;
 using MediaBrowser.Common.Logging;
+using MediaBrowser.Common.UI;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Progress;
 using MediaBrowser.Model.Progress;
-using System.Threading.Tasks;
-using MediaBrowser.Common.UI;
 
 
 namespace MediaBrowser.ServerApplication
 namespace MediaBrowser.ServerApplication
 {
 {

+ 6 - 4
MediaBrowser.TV/Providers/EpisodeImageFromMediaLocationProvider.cs

@@ -13,22 +13,24 @@ namespace MediaBrowser.TV.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class EpisodeImageFromMediaLocationProvider : BaseMetadataProvider
     public class EpisodeImageFromMediaLocationProvider : BaseMetadataProvider
     {
     {
-        public override bool Supports(BaseItem item)
+        public override bool Supports(BaseEntity item)
         {
         {
             return item is Episode;
             return item is Episode;
         }
         }
 
 
-        public override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             return Task.Run(() =>
             return Task.Run(() =>
             {
             {
+                Episode episode = item as Episode;
+
                 string metadataFolder = Path.Combine(args.Parent.Path, "metadata");
                 string metadataFolder = Path.Combine(args.Parent.Path, "metadata");
 
 
-                string episodeFileName = Path.GetFileName(item.Path);
+                string episodeFileName = Path.GetFileName(episode.Path);
 
 
                 Season season = args.Parent as Season;
                 Season season = args.Parent as Season;
 
 
-                SetPrimaryImagePath(item as Episode, season, metadataFolder, episodeFileName);
+                SetPrimaryImagePath(episode, season, metadataFolder, episodeFileName);
             });
             });
         }
         }
 
 

+ 6 - 4
MediaBrowser.TV/Providers/EpisodeProviderFromXml.cs

@@ -14,20 +14,22 @@ namespace MediaBrowser.TV.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class EpisodeProviderFromXml : BaseMetadataProvider
     public class EpisodeProviderFromXml : BaseMetadataProvider
     {
     {
-        public override bool Supports(BaseItem item)
+        public override bool Supports(BaseEntity item)
         {
         {
             return item is Episode;
             return item is Episode;
         }
         }
 
 
-        public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             string metadataFolder = Path.Combine(args.Parent.Path, "metadata");
             string metadataFolder = Path.Combine(args.Parent.Path, "metadata");
 
 
-            string episodeFileName = Path.GetFileName(item.Path);
+            Episode episode = item as Episode;
+
+            string episodeFileName = Path.GetFileName(episode.Path);
 
 
             string metadataFile = Path.Combine(metadataFolder, Path.ChangeExtension(episodeFileName, ".xml"));
             string metadataFile = Path.Combine(metadataFolder, Path.ChangeExtension(episodeFileName, ".xml"));
 
 
-            await FetchMetadata(item as Episode, args.Parent as Season, metadataFile);
+            await FetchMetadata(episode, args.Parent as Season, metadataFile);
         }
         }
 
 
         private async Task FetchMetadata(Episode item, Season season, string metadataFile)
         private async Task FetchMetadata(Episode item, Season season, string metadataFile)

+ 2 - 2
MediaBrowser.TV/Providers/SeriesProviderFromXml.cs

@@ -11,12 +11,12 @@ namespace MediaBrowser.TV.Providers
     [Export(typeof(BaseMetadataProvider))]
     [Export(typeof(BaseMetadataProvider))]
     public class SeriesProviderFromXml : BaseMetadataProvider
     public class SeriesProviderFromXml : BaseMetadataProvider
     {
     {
-        public override bool Supports(BaseItem item)
+        public override bool Supports(BaseEntity item)
         {
         {
             return item is Series;
             return item is Series;
         }
         }
 
 
-        public async override Task Fetch(BaseItem item, ItemResolveEventArgs args)
+        public async override Task Fetch(BaseEntity item, ItemResolveEventArgs args)
         {
         {
             var metadataFile = args.GetFileByName("series.xml");
             var metadataFile = args.GetFileByName("series.xml");