From 937d27ae9d6aa571ab9327f138bfba1b84c158db Mon Sep 17 00:00:00 2001 From: LukePulverenti Luke Pulverenti luke pulverenti Date: Sun, 19 Aug 2012 16:38:31 -0400 Subject: [PATCH] One async call leads to another, and another, all the way up the call stack... --- MediaBrowser.Api/ApiService.cs | 31 +++-- .../HttpHandlers/BaseMediaHandler.cs | 14 +-- MediaBrowser.Api/HttpHandlers/GenreHandler.cs | 9 +- .../HttpHandlers/GenresHandler.cs | 27 +---- MediaBrowser.Api/HttpHandlers/ImageHandler.cs | 114 ++++++++---------- MediaBrowser.Api/HttpHandlers/ItemHandler.cs | 5 +- .../HttpHandlers/ItemListHandler.cs | 7 +- .../HttpHandlers/PersonHandler.cs | 9 +- .../PluginConfigurationHandler.cs | 10 +- .../HttpHandlers/PluginsHandler.cs | 36 +++--- .../HttpHandlers/StudioHandler.cs | 9 +- .../HttpHandlers/StudiosHandler.cs | 27 +---- MediaBrowser.Api/HttpHandlers/UsersHandler.cs | 8 +- MediaBrowser.Api/HttpHandlers/YearHandler.cs | 9 +- MediaBrowser.Api/HttpHandlers/YearsHandler.cs | 27 +---- MediaBrowser.Common/Kernel/BaseKernel.cs | 28 ++++- .../Handlers/BaseEmbeddedResourceHandler.cs | 6 +- .../Net/Handlers/BaseHandler.cs | 61 ++++------ .../Net/Handlers/BaseJsonHandler.cs | 37 +++--- .../Net/Handlers/StaticFileHandler.cs | 58 +++++---- .../IO/DirectoryWatchers.cs | 9 +- MediaBrowser.Controller/Kernel.cs | 42 +++++++ .../Library/ItemController.cs | 62 +++++----- .../Providers/AudioInfoProvider.cs | 5 +- .../Providers/BaseMetadataProvider.cs | 23 +++- .../Providers/FolderProviderFromXml.cs | 4 +- .../ImageFromMediaLocationProvider.cs | 9 +- .../Providers/LocalTrailerProvider.cs | 11 +- .../Resolvers/BaseItemResolver.cs | 24 +--- .../Resolvers/FolderResolver.cs | 4 +- .../Resolvers/VirtualFolderResolver.cs | 4 +- .../Xml/BaseItemXmlParser.cs | 2 +- .../Providers/MovieProviderFromXml.cs | 4 +- .../Resolvers/MovieResolver.cs | 4 +- .../MainWindow.xaml.cs | 3 +- .../EpisodeImageFromMediaLocationProvider.cs | 10 +- .../Providers/EpisodeProviderFromXml.cs | 10 +- .../Providers/SeriesProviderFromXml.cs | 4 +- 38 files changed, 393 insertions(+), 373 deletions(-) diff --git a/MediaBrowser.Api/ApiService.cs b/MediaBrowser.Api/ApiService.cs index 4d764f3e39..765c6af1a7 100644 --- a/MediaBrowser.Api/ApiService.cs +++ b/MediaBrowser.Api/ApiService.cs @@ -1,8 +1,7 @@ using System; -using System.IO; +using System.Collections.Generic; using System.Linq; -using System.Reflection; -using MediaBrowser.Common.Configuration; +using System.Threading.Tasks; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; using MediaBrowser.Model.Entities; @@ -21,7 +20,7 @@ namespace MediaBrowser.Api return Kernel.Instance.GetItemById(guid); } - public static DTOBaseItem GetDTOBaseItem(BaseItem item, User user, + public async static Task GetDTOBaseItem(BaseItem item, User user, bool includeChildren = true, bool includePeople = true) { @@ -79,16 +78,16 @@ namespace MediaBrowser.Api dto.UserData = item.GetUserData(user); - AttachStudios(dto, item); + await AttachStudios(dto, item); if (includeChildren) { - AttachChildren(dto, item, user); + await AttachChildren(dto, item, user); } if (includePeople) { - AttachPeople(dto, item); + await AttachPeople(dto, item); } Folder folder = item as Folder; @@ -104,18 +103,20 @@ namespace MediaBrowser.Api 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) if (item.Studios != null) { + IEnumerable entities = await Task.WhenAll(item.Studios.Select(c => Kernel.Instance.ItemController.GetStudio(c))); + dto.Studios = item.Studios.Select(s => { BaseItemStudio baseItemStudio = new BaseItemStudio(); baseItemStudio.Name = s; - Studio ibnObject = Kernel.Instance.ItemController.GetStudio(s); + Studio ibnObject = entities.First(i => i.Name.Equals(s, StringComparison.OrdinalIgnoreCase)); 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; if (folder != null) { - dto.Children = folder.GetParentalAllowedChildren(user).Select(c => GetDTOBaseItem(c, user, false, false)); + IEnumerable children = folder.GetParentalAllowedChildren(user); + + dto.Children = await Task.WhenAll(children.Select(c => GetDTOBaseItem(c, user, false, false))); } 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) if (item.People != null) { + IEnumerable entities = await Task.WhenAll(item.People.Select(c => Kernel.Instance.ItemController.GetPerson(c.Name))); + dto.People = item.People.Select(p => { BaseItemPerson baseItemPerson = new BaseItemPerson(); 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) { diff --git a/MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs b/MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs index ffdbab65ed..d2dbd699d3 100644 --- a/MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/BaseMediaHandler.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Logging; using MediaBrowser.Common.Net; using MediaBrowser.Common.Net.Handlers; @@ -91,20 +90,17 @@ namespace MediaBrowser.Api.HttpHandlers } } - public override string ContentType + public override Task GetContentType() { - get + return Task.Run(() => { 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) diff --git a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs b/MediaBrowser.Api/HttpHandlers/GenreHandler.cs index 2b0d1c57d3..c4198b0288 100644 --- a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/GenreHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers /// public class GenreHandler : BaseJsonHandler> { - protected override IBNItem GetObjectToSerialize() + protected override async Task> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); @@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers string name = QueryString["name"]; - return GetGenre(parent, user, name); + return await GetGenre(parent, user, name); } /// /// Gets a Genre /// - private IBNItem GetGenre(Folder parent, User user, string name) + private async Task> GetGenre(Folder parent, User user, string name) { int count = 0; @@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers // Get the original entity so that we can also supply the PrimaryImagePath return new IBNItem() { - Item = Kernel.Instance.ItemController.GetGenre(name), + Item = await Kernel.Instance.ItemController.GetGenre(name), BaseItemCount = count }; } diff --git a/MediaBrowser.Api/HttpHandlers/GenresHandler.cs b/MediaBrowser.Api/HttpHandlers/GenresHandler.cs index 1134eb5689..cd68f1eaa9 100644 --- a/MediaBrowser.Api/HttpHandlers/GenresHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/GenresHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -10,20 +11,20 @@ namespace MediaBrowser.Api.HttpHandlers { public class GenresHandler : BaseJsonHandler>> { - protected override IEnumerable> GetObjectToSerialize() + protected override async Task>> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); User user = Kernel.Instance.Users.First(u => u.Id == userId); - return GetAllGenres(parent, user); + return await GetAllGenres(parent, user); } /// /// 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 /// - private IEnumerable> GetAllGenres(Folder parent, User user) + private async Task>> GetAllGenres(Folder parent, User user) { Dictionary data = new Dictionary(); @@ -52,25 +53,9 @@ namespace MediaBrowser.Api.HttpHandlers } } - // Now go through the dictionary and create a Category for each genre - List> list = new List>(); + IEnumerable entities = await Task.WhenAll(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() - { - Item = entity, - BaseItemCount = data[key] - }); - } - } - - return list; + return entities.Select(e => new IBNItem() { Item = e, BaseItemCount = data[e.Name] }); } } } diff --git a/MediaBrowser.Api/HttpHandlers/ImageHandler.cs b/MediaBrowser.Api/HttpHandlers/ImageHandler.cs index 8264380980..4a6b2a4817 100644 --- a/MediaBrowser.Api/HttpHandlers/ImageHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ImageHandler.cs @@ -13,39 +13,57 @@ namespace MediaBrowser.Api.HttpHandlers public class ImageHandler : BaseHandler { private string _ImagePath = null; - private string ImagePath + private async Task GetImagePath() { - get + if (_ImagePath == null) { - if (_ImagePath == null) - { - _ImagePath = GetImagePath(); - } - - return _ImagePath; + _ImagePath = await DiscoverImagePath(); } + + return _ImagePath; + } + + private async Task DiscoverImagePath() + { + string path = QueryString["path"] ?? string.Empty; + + if (!string.IsNullOrEmpty(path)) + { + return path; + } + + 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 Stream SourceStream + private async Task GetSourceStream() { - get - { - EnsureSourceStream(); - - return _SourceStream; - } + await EnsureSourceStream(); + return _SourceStream; } - private bool _SourceStreamEnsured = false; - private void EnsureSourceStream() + private async Task EnsureSourceStream() { if (!_SourceStreamEnsured) { try { - _SourceStream = File.OpenRead(ImagePath); + _SourceStream = File.OpenRead(await GetImagePath()); } catch (FileNotFoundException ex) { @@ -68,20 +86,17 @@ namespace MediaBrowser.Api.HttpHandlers } } } - - public override string ContentType - { - get - { - EnsureSourceStream(); - if (SourceStream == null) - { - return null; - } - - return MimeTypes.GetMimeType(ImagePath); + public async override Task GetContentType() + { + await EnsureSourceStream(); + + if (await GetSourceStream() == null) + { + return null; } + + return MimeTypes.GetMimeType(await GetImagePath()); } public override TimeSpan CacheDuration @@ -92,16 +107,16 @@ namespace MediaBrowser.Api.HttpHandlers } } - protected override DateTime? GetLastDateModified() + protected async override Task GetLastDateModified() { - EnsureSourceStream(); + await EnsureSourceStream(); - if (SourceStream == null) + if (await GetSourceStream() == null) { return null; } - return File.GetLastWriteTime(ImagePath); + return File.GetLastWriteTime(await GetImagePath()); } 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) diff --git a/MediaBrowser.Api/HttpHandlers/ItemHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemHandler.cs index dcbdf2a900..ac27633b7b 100644 --- a/MediaBrowser.Api/HttpHandlers/ItemHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ItemHandler.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -9,7 +10,7 @@ namespace MediaBrowser.Api.HttpHandlers { public class ItemHandler : BaseJsonHandler { - protected sealed override DTOBaseItem GetObjectToSerialize() + protected async override Task GetObjectToSerialize() { Guid userId = Guid.Parse(QueryString["userid"]); User user = Kernel.Instance.Users.First(u => u.Id == userId); @@ -21,7 +22,7 @@ namespace MediaBrowser.Api.HttpHandlers return null; } - return ApiService.GetDTOBaseItem(item, user); + return await ApiService.GetDTOBaseItem(item, user); } protected virtual BaseItem ItemToSerialize diff --git a/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs index 84141a6bf5..6e4a420813 100644 --- a/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ItemListHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -10,14 +11,14 @@ namespace MediaBrowser.Api.HttpHandlers { public class ItemListHandler : BaseJsonHandler> { - protected override IEnumerable GetObjectToSerialize() + protected override async Task> GetObjectToSerialize() { User user = Kernel.Instance.Users.First(u => u.Id == UserId); - return ItemsToSerialize.Select(i => + return await Task.WhenAll(ItemsToSerialize.Select(i => { return ApiService.GetDTOBaseItem(i, user, includeChildren: false, includePeople: false); - }); + })); } protected IEnumerable ItemsToSerialize diff --git a/MediaBrowser.Api/HttpHandlers/PersonHandler.cs b/MediaBrowser.Api/HttpHandlers/PersonHandler.cs index 75cccfac60..f095a3a47c 100644 --- a/MediaBrowser.Api/HttpHandlers/PersonHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PersonHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers /// public class PersonHandler : BaseJsonHandler> { - protected override IBNItem GetObjectToSerialize() + protected async override Task> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); @@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers string name = QueryString["name"]; - return GetPerson(parent, user, name); + return await GetPerson(parent, user, name); } /// /// Gets a Person /// - private IBNItem GetPerson(Folder parent, User user, string name) + private async Task> GetPerson(Folder parent, User user, string name) { int count = 0; @@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers // Get the original entity so that we can also supply the PrimaryImagePath return new IBNItem() { - Item = Kernel.Instance.ItemController.GetPerson(name), + Item = await Kernel.Instance.ItemController.GetPerson(name), BaseItemCount = count }; } diff --git a/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs index 0e19aecd5c..109440e982 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginConfigurationHandler.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.Plugins; @@ -8,11 +9,14 @@ namespace MediaBrowser.Api.HttpHandlers { public class PluginConfigurationHandler : BaseJsonHandler { - protected override BasePluginConfiguration GetObjectToSerialize() + protected override Task 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; + }); } } } diff --git a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs index c3c1b40493..f451832dd4 100644 --- a/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/PluginsHandler.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -11,26 +12,29 @@ namespace MediaBrowser.Api.HttpHandlers /// public class PluginsHandler : BaseJsonHandler> { - protected override IEnumerable GetObjectToSerialize() + protected override Task> 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); + } + + return plugins; }); - - if (QueryString["uionly"] == "1") - { - plugins = plugins.Where(p => p.DownloadToUI); - } - - return plugins; } } } diff --git a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs b/MediaBrowser.Api/HttpHandlers/StudioHandler.cs index 40daadc70f..b44970ea50 100644 --- a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/StudioHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers /// public class StudioHandler : BaseJsonHandler> { - protected override IBNItem GetObjectToSerialize() + protected async override Task> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); @@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers string name = QueryString["name"]; - return GetStudio(parent, user, name); + return await GetStudio(parent, user, name); } /// /// Gets a Studio /// - private IBNItem GetStudio(Folder parent, User user, string name) + private async Task> GetStudio(Folder parent, User user, string name) { int count = 0; @@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers // Get the original entity so that we can also supply the PrimaryImagePath return new IBNItem() { - Item = Kernel.Instance.ItemController.GetStudio(name), + Item = await Kernel.Instance.ItemController.GetStudio(name), BaseItemCount = count }; } diff --git a/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs b/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs index bef381000a..d379b45fb4 100644 --- a/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/StudiosHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -10,20 +11,20 @@ namespace MediaBrowser.Api.HttpHandlers { public class StudiosHandler : BaseJsonHandler>> { - protected override IEnumerable> GetObjectToSerialize() + protected override async Task>> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); User user = Kernel.Instance.Users.First(u => u.Id == userId); - return GetAllStudios(parent, user); + return await GetAllStudios(parent, user); } /// /// 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 /// - private IEnumerable> GetAllStudios(Folder parent, User user) + private async Task>> GetAllStudios(Folder parent, User user) { Dictionary data = new Dictionary(); @@ -52,25 +53,9 @@ namespace MediaBrowser.Api.HttpHandlers } } - // Now go through the dictionary and create a Category for each studio - List> list = new List>(); + IEnumerable entities = await Task.WhenAll(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() - { - Item = entity, - BaseItemCount = data[key] - }); - } - } - - return list; + return entities.Select(e => new IBNItem() { Item = e, BaseItemCount = data[e.Name] }); } } } diff --git a/MediaBrowser.Api/HttpHandlers/UsersHandler.cs b/MediaBrowser.Api/HttpHandlers/UsersHandler.cs index e1fe638ca8..1368735740 100644 --- a/MediaBrowser.Api/HttpHandlers/UsersHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/UsersHandler.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.Entities; @@ -7,9 +8,12 @@ namespace MediaBrowser.Api.HttpHandlers { class UsersHandler : BaseJsonHandler> { - protected override IEnumerable GetObjectToSerialize() + protected override Task> GetObjectToSerialize() { - return Kernel.Instance.Users; + return Task.Run(() => + { + return Kernel.Instance.Users; + }); } } } diff --git a/MediaBrowser.Api/HttpHandlers/YearHandler.cs b/MediaBrowser.Api/HttpHandlers/YearHandler.cs index 89cde99433..dfde600e78 100644 --- a/MediaBrowser.Api/HttpHandlers/YearHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/YearHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -13,7 +14,7 @@ namespace MediaBrowser.Api.HttpHandlers /// public class YearHandler : BaseJsonHandler> { - protected override IBNItem GetObjectToSerialize() + protected override async Task> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); @@ -21,13 +22,13 @@ namespace MediaBrowser.Api.HttpHandlers string year = QueryString["year"]; - return GetYear(parent, user, int.Parse(year)); + return await GetYear(parent, user, int.Parse(year)); } /// /// Gets a Year /// - private IBNItem GetYear(Folder parent, User user, int year) + private async Task> GetYear(Folder parent, User user, int year) { int count = 0; @@ -45,7 +46,7 @@ namespace MediaBrowser.Api.HttpHandlers // Get the original entity so that we can also supply the PrimaryImagePath return new IBNItem() { - Item = Kernel.Instance.ItemController.GetYear(year), + Item = await Kernel.Instance.ItemController.GetYear(year), BaseItemCount = count }; } diff --git a/MediaBrowser.Api/HttpHandlers/YearsHandler.cs b/MediaBrowser.Api/HttpHandlers/YearsHandler.cs index 3d9ae59f2d..6ce087f857 100644 --- a/MediaBrowser.Api/HttpHandlers/YearsHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/YearsHandler.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Common.Net.Handlers; using MediaBrowser.Controller; using MediaBrowser.Model.DTO; @@ -10,20 +11,20 @@ namespace MediaBrowser.Api.HttpHandlers { public class YearsHandler : BaseJsonHandler>> { - protected override IEnumerable> GetObjectToSerialize() + protected override async Task>> GetObjectToSerialize() { Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; Guid userId = Guid.Parse(QueryString["userid"]); User user = Kernel.Instance.Users.First(u => u.Id == userId); - return GetAllYears(parent, user); + return await GetAllYears(parent, user); } /// /// 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 /// - private IEnumerable> GetAllYears(Folder parent, User user) + private async Task>> GetAllYears(Folder parent, User user) { Dictionary data = new Dictionary(); @@ -49,25 +50,9 @@ namespace MediaBrowser.Api.HttpHandlers } } - // Now go through the dictionary and create a Category for each studio - List> list = new List>(); + IEnumerable entities = await Task.WhenAll(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() - { - Item = entity, - BaseItemCount = data[key] - }); - } - } - - return list; + return entities.Select(e => new IBNItem() { Item = e, BaseItemCount = data[int.Parse(e.Name)] }); } } } diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 7b6f6844ce..5b2f97ced3 100644 --- a/MediaBrowser.Common/Kernel/BaseKernel.cs +++ b/MediaBrowser.Common/Kernel/BaseKernel.cs @@ -2,17 +2,16 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; -using System.Configuration; using System.IO; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Logging; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Progress; -using System.Threading.Tasks; namespace MediaBrowser.Common.Kernel { @@ -93,6 +92,8 @@ namespace MediaBrowser.Common.Kernel /// protected void ReloadComposableParts() { + DisposeComposableParts(); + // 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 IEnumerable pluginAssemblies = Directory.GetFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories).Select(f => Assembly.Load(File.ReadAllBytes((f)))); @@ -203,10 +204,33 @@ namespace MediaBrowser.Common.Kernel /// public virtual void Dispose() { + DisposeComposableParts(); DisposeHttpServer(); DisposeLogger(); } + /// + /// Disposes all objects gathered through MEF composable parts + /// + protected virtual void DisposeComposableParts() + { + DisposePlugins(); + } + + /// + /// Disposes all plugins + /// + private void DisposePlugins() + { + if (Plugins != null) + { + foreach (BasePlugin plugin in Plugins) + { + plugin.Dispose(); + } + } + } + /// /// Disposes the current HttpServer /// diff --git a/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs index 2fcead05b0..a8c7090f7d 100644 --- a/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs +++ b/MediaBrowser.Common/Net/Handlers/BaseEmbeddedResourceHandler.cs @@ -14,9 +14,9 @@ namespace MediaBrowser.Common.Net.Handlers protected string ResourcePath { get; set; } - public override string ContentType + public override Task GetContentType() { - get + return Task.Run(() => { string extension = Path.GetExtension(ResourcePath); @@ -46,7 +46,7 @@ namespace MediaBrowser.Common.Net.Handlers } return "text/plain; charset=utf-8"; - } + }); } protected override Task WriteResponseToOutputStream(Stream stream) diff --git a/MediaBrowser.Common/Net/Handlers/BaseHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseHandler.cs index f9ef065ecc..7a4efdaf2b 100644 --- a/MediaBrowser.Common/Net/Handlers/BaseHandler.cs +++ b/MediaBrowser.Common/Net/Handlers/BaseHandler.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Common.Net.Handlers /// /// Gets the MIME type to include in the response headers /// - public abstract string ContentType { get; } + public abstract Task GetContentType(); /// /// 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; - /// - /// 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. - /// - 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 @@ -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 StatusCode = SupportsByteRangeRequests && IsRangeRequest ? 206 : 200; - ctx.Response.ContentType = ContentType; + ctx.Response.ContentType = await GetContentType(); TimeSpan cacheDuration = CacheDuration; + DateTime? lastDateModified = await GetLastDateModified(); + if (ctx.Request.Headers.AllKeys.Contains("If-Modified-Since")) { 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 the cache hasn't expired yet just return a 304 - if (IsCacheValid(ifModifiedSince, cacheDuration, LastDateModified)) + if (IsCacheValid(ifModifiedSince, cacheDuration, lastDateModified)) { StatusCode = 304; } } } - PrepareResponse(); + await PrepareResponse(); if (IsResponseValid) { - await ProcessUncachedRequest(ctx, cacheDuration); + bool compressResponse = ShouldCompressResponse(ctx.Response.ContentType) && ClientSupportsCompression; + + await ProcessUncachedRequest(ctx, compressResponse, cacheDuration, lastDateModified); } 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 ctx.Response.StatusCode = 500; - + Logger.LogException(ex); } 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; @@ -270,7 +252,7 @@ namespace MediaBrowser.Common.Net.Handlers } // Add the compression header - if (CompressResponse && ClientSupportsCompression) + if (compressResponse) { ctx.Response.AddHeader("Content-Encoding", CompressionMethod); } @@ -278,7 +260,7 @@ namespace MediaBrowser.Common.Net.Handlers // Add caching headers if (cacheDuration.Ticks > 0) { - CacheResponse(ctx.Response, cacheDuration, LastDateModified); + CacheResponse(ctx.Response, cacheDuration, lastDateModified); } // Set the status code @@ -289,7 +271,7 @@ namespace MediaBrowser.Common.Net.Handlers // Finally, write the response data Stream outputStream = ctx.Response.OutputStream; - if (CompressResponse && ClientSupportsCompression) + if (compressResponse) { if (CompressionMethod.Equals("deflate", StringComparison.OrdinalIgnoreCase)) { @@ -321,10 +303,11 @@ namespace MediaBrowser.Common.Net.Handlers } /// - /// 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 /// - protected virtual void PrepareResponse() + protected virtual Task PrepareResponse() { + return Task.Run(() => { }); } protected abstract Task WriteResponseToOutputStream(Stream stream); @@ -372,9 +355,11 @@ namespace MediaBrowser.Common.Net.Handlers return null; } - protected virtual DateTime? GetLastDateModified() + protected virtual Task GetLastDateModified() { - return null; + DateTime? value = null; + + return Task.Run(() => { return value; }); } private bool IsResponseValid diff --git a/MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs index a35af92313..a3a47a4a33 100644 --- a/MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs +++ b/MediaBrowser.Common/Net/Handlers/BaseJsonHandler.cs @@ -6,19 +6,22 @@ namespace MediaBrowser.Common.Net.Handlers { public abstract class BaseJsonHandler : BaseHandler { - public override string ContentType + public override Task GetContentType() { - get { return MimeTypes.JsonMimeType; } + return Task.Run(() => + { + return MimeTypes.JsonMimeType; + }); } private bool _ObjectToSerializeEnsured = false; private T _ObjectToSerialize; - private void EnsureObjectToSerialize() + private async Task EnsureObjectToSerialize() { if (!_ObjectToSerializeEnsured) { - _ObjectToSerialize = GetObjectToSerialize(); + _ObjectToSerialize = await GetObjectToSerialize(); if (_ObjectToSerialize == null) { @@ -29,30 +32,18 @@ namespace MediaBrowser.Common.Net.Handlers } } - private T ObjectToSerialize + protected abstract Task GetObjectToSerialize(); + + protected override async Task PrepareResponse() { - get - { - EnsureObjectToSerialize(); - return _ObjectToSerialize; - } + await EnsureObjectToSerialize(); } - protected abstract T GetObjectToSerialize(); - - protected override void PrepareResponse() + protected async override Task WriteResponseToOutputStream(Stream stream) { - base.PrepareResponse(); + await EnsureObjectToSerialize(); - EnsureObjectToSerialize(); - } - - protected override Task WriteResponseToOutputStream(Stream stream) - { - return Task.Run(() => - { - JsonSerializer.SerializeToStream(ObjectToSerialize, stream); - }); + JsonSerializer.SerializeToStream(_ObjectToSerialize, stream); } } } diff --git a/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs b/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs index 5656f92739..d8971dd97d 100644 --- a/MediaBrowser.Common/Net/Handlers/StaticFileHandler.cs +++ b/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; - } - - // It will take some work to support compression within this handler return false; } + + // Don't compress media + if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // It will take some work to support compression within this handler + return false; } protected override long? GetTotalContentLength() @@ -105,31 +100,32 @@ namespace MediaBrowser.Common.Net.Handlers return SourceStream.Length; } - protected override DateTime? GetLastDateModified() + protected override Task GetLastDateModified() { - EnsureSourceStream(); - - if (SourceStream == null) + return Task.Run(() => { - return null; - } + EnsureSourceStream(); - return File.GetLastWriteTime(Path); + if (SourceStream == null) + { + return null; + } + + return File.GetLastWriteTime(Path); + }); } - public override string ContentType + public override Task GetContentType() { - get + return Task.Run(() => { 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) diff --git a/MediaBrowser.Controller/IO/DirectoryWatchers.cs b/MediaBrowser.Controller/IO/DirectoryWatchers.cs index 1ca9cf0c85..e4eadbbd0c 100644 --- a/MediaBrowser.Controller/IO/DirectoryWatchers.cs +++ b/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 = null; @@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.IO List paths = affectedPaths; affectedPaths = new List(); - //ProcessPathChanges(paths); + await ProcessPathChanges(paths); } private async Task ProcessPathChanges(IEnumerable paths) @@ -109,10 +109,7 @@ namespace MediaBrowser.Controller.IO } else { - /*Parallel.For(0, itemsToRefresh.Count, i => - { - Kernel.Instance.ReloadItem(itemsToRefresh[i]); - });*/ + await Task.WhenAll(itemsToRefresh.Select(i => Kernel.Instance.ReloadItem(i))); } } diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index b696abef54..57e30479b3 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -248,5 +248,47 @@ namespace MediaBrowser.Controller 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(); + } + + /// + /// Disposes all providers + /// + private void DisposeProviders() + { + if (MetadataProviders != null) + { + foreach (var provider in MetadataProviders) + { + provider.Dispose(); + } + } + } } } diff --git a/MediaBrowser.Controller/Library/ItemController.cs b/MediaBrowser.Controller/Library/ItemController.cs index 4b0d9a9835..bc5cea79b9 100644 --- a/MediaBrowser.Controller/Library/ItemController.cs +++ b/MediaBrowser.Controller/Library/ItemController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -217,49 +218,49 @@ namespace MediaBrowser.Controller.Library /// /// Gets a Person /// - public Person GetPerson(string name) + public async Task GetPerson(string name) { string path = Path.Combine(Kernel.Instance.ApplicationPaths.PeoplePath, name); - return GetImagesByNameItem(path, name); + return await GetImagesByNameItem(path, name); } /// /// Gets a Studio /// - public Studio GetStudio(string name) + public async Task GetStudio(string name) { string path = Path.Combine(Kernel.Instance.ApplicationPaths.StudioPath, name); - return GetImagesByNameItem(path, name); + return await GetImagesByNameItem(path, name); } /// /// Gets a Genre /// - public Genre GetGenre(string name) + public async Task GetGenre(string name) { string path = Path.Combine(Kernel.Instance.ApplicationPaths.GenrePath, name); - return GetImagesByNameItem(path, name); + return await GetImagesByNameItem(path, name); } /// /// Gets a Year /// - public Year GetYear(int value) + public async Task GetYear(int value) { string path = Path.Combine(Kernel.Instance.ApplicationPaths.YearPath, value.ToString()); - return GetImagesByNameItem(path, value.ToString()); + return await GetImagesByNameItem(path, value.ToString()); } - private Dictionary ImagesByNameItemCache = new Dictionary(); + private ConcurrentDictionary ImagesByNameItemCache = new ConcurrentDictionary(); /// /// Generically retrieves an IBN item /// - private T GetImagesByNameItem(string path, string name) + private async Task GetImagesByNameItem(string path, string name) where T : BaseEntity, new() { 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 if (!ImagesByNameItemCache.ContainsKey(key)) { - ImagesByNameItemCache[key] = CreateImagesByNameItem(path, name); + T obj = await CreateImagesByNameItem(path, name); + ImagesByNameItemCache[key] = obj; + return obj; } return ImagesByNameItemCache[key] as T; @@ -276,7 +279,7 @@ namespace MediaBrowser.Controller.Library /// /// Creates an IBN item based on a given path /// - private T CreateImagesByNameItem(string path, string name) + private async Task CreateImagesByNameItem(string path, string name) where T : BaseEntity, new() { T item = new T(); @@ -284,25 +287,28 @@ namespace MediaBrowser.Controller.Library item.Name = name; 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 - { - DateTime now = DateTime.Now; - item.DateCreated = now; - item.DateModified = now; + 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"); + } + + var b = false; + + if (b) + { + await Kernel.Instance.ExecuteMetadataProviders(item, null); } return item; diff --git a/MediaBrowser.Controller/Providers/AudioInfoProvider.cs b/MediaBrowser.Controller/Providers/AudioInfoProvider.cs index 934f082d54..70adb688f1 100644 --- a/MediaBrowser.Controller/Providers/AudioInfoProvider.cs +++ b/MediaBrowser.Controller/Providers/AudioInfoProvider.cs @@ -12,12 +12,12 @@ namespace MediaBrowser.Controller.Providers [Export(typeof(BaseMetadataProvider))] public class AudioInfoProvider : BaseMetadataProvider { - public override bool Supports(BaseItem item) + public override bool Supports(BaseEntity item) { 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; @@ -62,6 +62,7 @@ namespace MediaBrowser.Controller.Providers { 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++) { EnsureDirectory(Path.Combine(Kernel.Instance.ApplicationPaths.FFProbeAudioCacheDirectory, i.ToString())); diff --git a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs index 93d9ef10e4..e40d30372d 100644 --- a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs +++ b/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.Model.Entities; namespace MediaBrowser.Controller.Providers { - public abstract class BaseMetadataProvider + public abstract class BaseMetadataProvider : IDisposable { /// /// If the provider needs any startup routines, add them here @@ -13,11 +14,23 @@ namespace MediaBrowser.Controller.Providers { } - public virtual bool Supports(BaseItem item) + /// + /// Disposes anything created during Init + /// + public virtual void Dispose() { - return true; } - public abstract Task Fetch(BaseItem item, ItemResolveEventArgs args); + public abstract bool Supports(BaseEntity item); + + public virtual bool RequiresInternet + { + get + { + return false; + } + } + + public abstract Task Fetch(BaseEntity item, ItemResolveEventArgs args); } } diff --git a/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs b/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs index 14067dd202..2ef2142375 100644 --- a/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs +++ b/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs @@ -9,12 +9,12 @@ namespace MediaBrowser.Controller.Providers [Export(typeof(BaseMetadataProvider))] public class FolderProviderFromXml : BaseMetadataProvider { - public override bool Supports(BaseItem item) + public override bool Supports(BaseEntity item) { 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"); diff --git a/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs index 2df07251af..5b086f795a 100644 --- a/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs +++ b/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs @@ -12,13 +12,18 @@ namespace MediaBrowser.Controller.Providers [Export(typeof(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(() => { if (args.IsFolder) { - PopulateImages(item, args); + PopulateImages(item as BaseItem, args); } }); } diff --git a/MediaBrowser.Controller/Providers/LocalTrailerProvider.cs b/MediaBrowser.Controller/Providers/LocalTrailerProvider.cs index 027c2f75d2..9d909934d4 100644 --- a/MediaBrowser.Controller/Providers/LocalTrailerProvider.cs +++ b/MediaBrowser.Controller/Providers/LocalTrailerProvider.cs @@ -10,8 +10,15 @@ namespace MediaBrowser.Controller.Providers [Export(typeof(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"); if (trailerPath.HasValue) @@ -32,7 +39,7 @@ namespace MediaBrowser.Controller.Providers } } - item.LocalTrailers = localTrailers; + baseItem.LocalTrailers = localTrailers; } } } diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs index 75e516487d..d8f6b61ce9 100644 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs @@ -2,13 +2,12 @@ using System.IO; using System.Threading.Tasks; using MediaBrowser.Controller.Events; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Resolvers { public abstract class BaseItemResolver : IBaseItemResolver - where T : BaseItem, new () + where T : BaseItem, new() { protected virtual T Resolve(ItemResolveEventArgs args) { @@ -18,7 +17,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// Sets initial values on the newly resolved item /// - protected virtual void SetItemValues(T item, ItemResolveEventArgs args) + protected virtual void SetInitialItemValues(T item, ItemResolveEventArgs args) { // If the subclass didn't specify this if (string.IsNullOrEmpty(item.Path)) @@ -38,35 +37,24 @@ namespace MediaBrowser.Controller.Resolvers public async Task ResolvePath(ItemResolveEventArgs args) { T item = Resolve(args); - + if (item != null) { // Set initial values on the newly resolved item - SetItemValues(item, args); + SetInitialItemValues(item, args); // Make sure the item has a name EnsureName(item); // Make sure DateCreated and DateModified have values EnsureDates(item); - - await FetchMetadataFromProviders(item, args); + + await Kernel.Instance.ExecuteMetadataProviders(item, args); } 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) { // If the subclass didn't supply a name, add it here diff --git a/MediaBrowser.Controller/Resolvers/FolderResolver.cs b/MediaBrowser.Controller/Resolvers/FolderResolver.cs index ff326669f3..858b3bcd8d 100644 --- a/MediaBrowser.Controller/Resolvers/FolderResolver.cs +++ b/MediaBrowser.Controller/Resolvers/FolderResolver.cs @@ -21,9 +21,9 @@ namespace MediaBrowser.Controller.Resolvers public abstract class BaseFolderResolver : BaseItemResolver 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; } diff --git a/MediaBrowser.Controller/Resolvers/VirtualFolderResolver.cs b/MediaBrowser.Controller/Resolvers/VirtualFolderResolver.cs index 51478fd02e..c81cb204a7 100644 --- a/MediaBrowser.Controller/Resolvers/VirtualFolderResolver.cs +++ b/MediaBrowser.Controller/Resolvers/VirtualFolderResolver.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Resolvers 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=...] // 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(']'); } - base.SetItemValues(item, args); + base.SetInitialItemValues(item, args); } } diff --git a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs index 0e514b8b21..6c7a265fac 100644 --- a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Xml; using MediaBrowser.Model.Entities; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Xml { diff --git a/MediaBrowser.Movies/Providers/MovieProviderFromXml.cs b/MediaBrowser.Movies/Providers/MovieProviderFromXml.cs index 4e0bacb054..03c0814549 100644 --- a/MediaBrowser.Movies/Providers/MovieProviderFromXml.cs +++ b/MediaBrowser.Movies/Providers/MovieProviderFromXml.cs @@ -11,12 +11,12 @@ namespace MediaBrowser.Movies.Providers [Export(typeof(BaseMetadataProvider))] public class MovieProviderFromXml : BaseMetadataProvider { - public override bool Supports(BaseItem item) + public override bool Supports(BaseEntity item) { 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"); diff --git a/MediaBrowser.Movies/Resolvers/MovieResolver.cs b/MediaBrowser.Movies/Resolvers/MovieResolver.cs index 36fbecf68b..498597c978 100644 --- a/MediaBrowser.Movies/Resolvers/MovieResolver.cs +++ b/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); } diff --git a/MediaBrowser.ServerApplication/MainWindow.xaml.cs b/MediaBrowser.ServerApplication/MainWindow.xaml.cs index abca9149f1..67e20b2a21 100644 --- a/MediaBrowser.ServerApplication/MainWindow.xaml.cs +++ b/MediaBrowser.ServerApplication/MainWindow.xaml.cs @@ -2,10 +2,9 @@ using System.Diagnostics; using System.Windows; using MediaBrowser.Common.Logging; +using MediaBrowser.Common.UI; using MediaBrowser.Controller; using MediaBrowser.Model.Progress; -using System.Threading.Tasks; -using MediaBrowser.Common.UI; namespace MediaBrowser.ServerApplication { diff --git a/MediaBrowser.TV/Providers/EpisodeImageFromMediaLocationProvider.cs b/MediaBrowser.TV/Providers/EpisodeImageFromMediaLocationProvider.cs index 61525dc790..8babcec630 100644 --- a/MediaBrowser.TV/Providers/EpisodeImageFromMediaLocationProvider.cs +++ b/MediaBrowser.TV/Providers/EpisodeImageFromMediaLocationProvider.cs @@ -13,22 +13,24 @@ namespace MediaBrowser.TV.Providers [Export(typeof(BaseMetadataProvider))] public class EpisodeImageFromMediaLocationProvider : BaseMetadataProvider { - public override bool Supports(BaseItem item) + public override bool Supports(BaseEntity item) { return item is Episode; } - public override Task Fetch(BaseItem item, ItemResolveEventArgs args) + public override Task Fetch(BaseEntity item, ItemResolveEventArgs args) { return Task.Run(() => { + Episode episode = item as Episode; + 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; - SetPrimaryImagePath(item as Episode, season, metadataFolder, episodeFileName); + SetPrimaryImagePath(episode, season, metadataFolder, episodeFileName); }); } diff --git a/MediaBrowser.TV/Providers/EpisodeProviderFromXml.cs b/MediaBrowser.TV/Providers/EpisodeProviderFromXml.cs index 4c6c584597..c92b1c4754 100644 --- a/MediaBrowser.TV/Providers/EpisodeProviderFromXml.cs +++ b/MediaBrowser.TV/Providers/EpisodeProviderFromXml.cs @@ -14,20 +14,22 @@ namespace MediaBrowser.TV.Providers [Export(typeof(BaseMetadataProvider))] public class EpisodeProviderFromXml : BaseMetadataProvider { - public override bool Supports(BaseItem item) + public override bool Supports(BaseEntity item) { 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 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")); - 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) diff --git a/MediaBrowser.TV/Providers/SeriesProviderFromXml.cs b/MediaBrowser.TV/Providers/SeriesProviderFromXml.cs index 7adfb0483b..02d5ce1782 100644 --- a/MediaBrowser.TV/Providers/SeriesProviderFromXml.cs +++ b/MediaBrowser.TV/Providers/SeriesProviderFromXml.cs @@ -11,12 +11,12 @@ namespace MediaBrowser.TV.Providers [Export(typeof(BaseMetadataProvider))] public class SeriesProviderFromXml : BaseMetadataProvider { - public override bool Supports(BaseItem item) + public override bool Supports(BaseEntity item) { 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");