diff --git a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemsWithGenreHandler.cs similarity index 87% rename from MediaBrowser.Api/HttpHandlers/GenreHandler.cs rename to MediaBrowser.Api/HttpHandlers/ItemsWithGenreHandler.cs index 477705e827..16bb561967 100644 --- a/MediaBrowser.Api/HttpHandlers/GenreHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ItemsWithGenreHandler.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Gets all items within a Genre /// - public class GenreHandler : ItemListHandler + public class ItemsWithGenreHandler : ItemListHandler { protected override IEnumerable ItemsToSerialize { diff --git a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemsWithStudioHandler.cs similarity index 87% rename from MediaBrowser.Api/HttpHandlers/StudioHandler.cs rename to MediaBrowser.Api/HttpHandlers/ItemsWithStudioHandler.cs index 019ced0281..30120d5248 100644 --- a/MediaBrowser.Api/HttpHandlers/StudioHandler.cs +++ b/MediaBrowser.Api/HttpHandlers/ItemsWithStudioHandler.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Api.HttpHandlers /// /// Gets all items within containing a studio /// - public class StudioHandler : ItemListHandler + public class ItemsWithStudioHandler : ItemListHandler { protected override IEnumerable ItemsToSerialize { diff --git a/MediaBrowser.Api/HttpHandlers/ItemsWithYearHandler.cs b/MediaBrowser.Api/HttpHandlers/ItemsWithYearHandler.cs new file mode 100644 index 0000000000..d5d4df4446 --- /dev/null +++ b/MediaBrowser.Api/HttpHandlers/ItemsWithYearHandler.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using MediaBrowser.Controller; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Api.HttpHandlers +{ + /// + /// Gets all items within containing a studio + /// + public class ItemsWithYearHandler : ItemListHandler + { + protected override IEnumerable ItemsToSerialize + { + get + { + Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; + + return Kernel.Instance.GetItemsWithYear(parent, int.Parse(QueryString["name"]), UserId); + } + } + } +} diff --git a/MediaBrowser.Api/HttpHandlers/YearsHandler.cs b/MediaBrowser.Api/HttpHandlers/YearsHandler.cs new file mode 100644 index 0000000000..6f47870282 --- /dev/null +++ b/MediaBrowser.Api/HttpHandlers/YearsHandler.cs @@ -0,0 +1,20 @@ +using System; +using MediaBrowser.Controller; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Api.HttpHandlers +{ + public class YearsHandler : JsonHandler + { + protected override object ObjectToSerialize + { + get + { + Folder parent = ApiService.GetItemById(QueryString["id"]) as Folder; + Guid userId = Guid.Parse(QueryString["userid"]); + + return Kernel.Instance.GetAllYears(parent, userId); + } + } + } +} diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 544dec6a9c..0a295fba46 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -49,7 +49,7 @@ - + @@ -60,11 +60,13 @@ - + + + diff --git a/MediaBrowser.Api/Plugin.cs b/MediaBrowser.Api/Plugin.cs index 3533253166..efbd9725eb 100644 --- a/MediaBrowser.Api/Plugin.cs +++ b/MediaBrowser.Api/Plugin.cs @@ -49,17 +49,25 @@ namespace MediaBrowser.Api { return new UsersHandler(); } - else if (localPath.EndsWith("/api/genre", StringComparison.OrdinalIgnoreCase)) + else if (localPath.EndsWith("/api/itemswithgenre", StringComparison.OrdinalIgnoreCase)) { - return new GenreHandler(); + return new ItemsWithGenreHandler(); } else if (localPath.EndsWith("/api/genres", StringComparison.OrdinalIgnoreCase)) { return new GenresHandler(); } - else if (localPath.EndsWith("/api/studio", StringComparison.OrdinalIgnoreCase)) + else if (localPath.EndsWith("/api/itemswithyear", StringComparison.OrdinalIgnoreCase)) { - return new StudioHandler(); + return new ItemsWithYearHandler(); + } + else if (localPath.EndsWith("/api/years", StringComparison.OrdinalIgnoreCase)) + { + return new YearsHandler(); + } + else if (localPath.EndsWith("/api/itemswithstudio", StringComparison.OrdinalIgnoreCase)) + { + return new ItemsWithStudioHandler(); } else if (localPath.EndsWith("/api/studios", StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.ApiInteraction/ApiClient.cs b/MediaBrowser.ApiInteraction/ApiClient.cs index 5770ee2a41..f8c9170a72 100644 --- a/MediaBrowser.ApiInteraction/ApiClient.cs +++ b/MediaBrowser.ApiInteraction/ApiClient.cs @@ -184,15 +184,41 @@ namespace MediaBrowser.ApiInteraction } /// - /// Gets a Genre + /// Gets all Years /// - public async Task> GetGenreAsync(string name, Guid userId) + public async Task>> GetAllYearsAsync(Guid userId) { - string url = ApiUrl + "/genre?userId=" + userId.ToString() + "&name=" + name; + string url = ApiUrl + "/years?userId=" + userId.ToString(); using (Stream stream = await HttpClient.GetStreamAsync(url)) { - return JsonSerializer.DeserializeFromStream>(stream); + return JsonSerializer.DeserializeFromStream>>(stream); + } + } + + /// + /// Gets a Year + /// + public async Task>> GetItemsWithYearAsync(string name, Guid userId) + { + string url = ApiUrl + "/itemswithyear?userId=" + userId.ToString() + "&name=" + name; + + using (Stream stream = await HttpClient.GetStreamAsync(url)) + { + return JsonSerializer.DeserializeFromStream>>(stream); + } + } + + /// + /// Gets a Genre + /// + public async Task>> GetItemsWithGenreAsync(string name, Guid userId) + { + string url = ApiUrl + "/itemswithgenre?userId=" + userId.ToString() + "&name=" + name; + + using (Stream stream = await HttpClient.GetStreamAsync(url)) + { + return JsonSerializer.DeserializeFromStream>>(stream); } } @@ -225,13 +251,13 @@ namespace MediaBrowser.ApiInteraction /// /// Gets a Studio /// - public async Task> GetStudioAsync(string name, Guid userId) + public async Task>> GetItemsWithStudioAsync(string name, Guid userId) { - string url = ApiUrl + "/studio?userId=" + userId.ToString() + "&name=" + name; + string url = ApiUrl + "/itemswithstudio?userId=" + userId.ToString() + "&name=" + name; using (Stream stream = await HttpClient.GetStreamAsync(url)) { - return JsonSerializer.DeserializeFromStream>(stream); + return JsonSerializer.DeserializeFromStream>>(stream); } } } diff --git a/MediaBrowser.Common/Configuration/ApplicationPaths.cs b/MediaBrowser.Common/Configuration/ApplicationPaths.cs index 43cf4cf882..dc87749cfb 100644 --- a/MediaBrowser.Common/Configuration/ApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/ApplicationPaths.cs @@ -175,7 +175,7 @@ namespace MediaBrowser.Common.Configuration /// /// Gets the path to the Images By Name directory /// - public static string IBNPath + public static string ImagesByNamePath { get { @@ -192,6 +192,90 @@ namespace MediaBrowser.Common.Configuration } } + private static string _PeoplePath; + /// + /// Gets the path to the People directory + /// + public static string PeoplePath + { + get + { + if (_PeoplePath == null) + { + _PeoplePath = Path.Combine(ImagesByNamePath, "People"); + if (!Directory.Exists(_PeoplePath)) + { + Directory.CreateDirectory(_PeoplePath); + } + } + + return _PeoplePath; + } + } + + private static string _GenrePath; + /// + /// Gets the path to the Genre directory + /// + public static string GenrePath + { + get + { + if (_GenrePath == null) + { + _GenrePath = Path.Combine(ImagesByNamePath, "Genre"); + if (!Directory.Exists(_GenrePath)) + { + Directory.CreateDirectory(_GenrePath); + } + } + + return _GenrePath; + } + } + + private static string _StudioPath; + /// + /// Gets the path to the Studio directory + /// + public static string StudioPath + { + get + { + if (_StudioPath == null) + { + _StudioPath = Path.Combine(ImagesByNamePath, "Studio"); + if (!Directory.Exists(_StudioPath)) + { + Directory.CreateDirectory(_StudioPath); + } + } + + return _StudioPath; + } + } + + private static string _yearPath; + /// + /// Gets the path to the Year directory + /// + public static string YearPath + { + get + { + if (_yearPath == null) + { + _yearPath = Path.Combine(ImagesByNamePath, "Year"); + if (!Directory.Exists(_yearPath)) + { + Directory.CreateDirectory(_yearPath); + } + } + + return _yearPath; + } + } + /// /// Gets the path to the application's ProgramDataFolder /// diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 8eed678ba3..a8baec8351 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -314,6 +314,14 @@ namespace MediaBrowser.Controller return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.Genres != null && f.Genres.Any(s => s.Equals(genre, StringComparison.OrdinalIgnoreCase))); } + /// + /// Finds all recursive items within a top-level parent that contain the given year and are allowed for the current user + /// + public IEnumerable GetItemsWithYear(Folder parent, int year, Guid userId) + { + return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.ProductionYear.HasValue && f.ProductionYear == year); + } + /// /// Finds all recursive items within a top-level parent that contain the given person and are allowed for the current user /// @@ -322,6 +330,57 @@ namespace MediaBrowser.Controller return GetParentalAllowedRecursiveChildren(parent, userId).Where(f => f.People != null && f.People.Any(s => s.Name.Equals(personName, StringComparison.OrdinalIgnoreCase))); } + /// + /// 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 + /// + public IEnumerable> GetAllYears(Folder parent, Guid userId) + { + Dictionary data = new Dictionary(); + + // Get all the allowed recursive children + IEnumerable allItems = Kernel.Instance.GetParentalAllowedRecursiveChildren(parent, userId); + + foreach (var item in allItems) + { + // Add the year from the item to the data dictionary + // If the year already exists, increment the count + if (item.ProductionYear == null) + { + continue; + } + + if (!data.ContainsKey(item.ProductionYear.Value)) + { + data.Add(item.ProductionYear.Value, 1); + } + else + { + data[item.ProductionYear.Value]++; + } + } + + // Now go through the dictionary and create a Category for each studio + List> list = new List>(); + + 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 CategoryInfo() + { + Item = entity, + ItemCount = data[key] + }); + } + } + + return list; + } + /// /// 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 @@ -441,7 +500,7 @@ namespace MediaBrowser.Controller User user = new User(); user.Name = "Default User"; - user.Id = Guid.NewGuid(); + user.Id = Guid.Parse("5d1cf7fce25943b790d140095457a42b"); list.Add(user); diff --git a/MediaBrowser.Controller/Library/ItemController.cs b/MediaBrowser.Controller/Library/ItemController.cs index cfca9e2ccb..0c62ce7d9a 100644 --- a/MediaBrowser.Controller/Library/ItemController.cs +++ b/MediaBrowser.Controller/Library/ItemController.cs @@ -8,6 +8,7 @@ using MediaBrowser.Controller.Events; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; +using MediaBrowser.Common.Configuration; namespace MediaBrowser.Controller.Library { @@ -57,41 +58,41 @@ namespace MediaBrowser.Controller.Library } #endregion - #region Item Events + #region BaseItem Events /// /// Called when an item is being created. /// This should be used to fill item values, such as metadata /// - public event EventHandler> ItemCreating; + public event EventHandler> BaseItemCreating; /// /// Called when an item has been created. /// This should be used to process or modify item values. /// - public event EventHandler> ItemCreated; + public event EventHandler> BaseItemCreated; #endregion /// /// Called when an item has been created /// - private void OnItemCreated(BaseItem item, Folder parent) + private void OnBaseItemCreated(BaseItem item, Folder parent) { GenericItemEventArgs args = new GenericItemEventArgs { Item = item }; - if (ItemCreating != null) + if (BaseItemCreating != null) { - ItemCreating(this, args); + BaseItemCreating(this, args); } - if (ItemCreated != null) + if (BaseItemCreated != null) { - ItemCreated(this, args); + BaseItemCreated(this, args); } } private void FireCreateEventsRecursive(Folder folder, Folder parent) { - OnItemCreated(folder, parent); + OnBaseItemCreated(folder, parent); int count = folder.Children.Length; @@ -107,7 +108,7 @@ namespace MediaBrowser.Controller.Library } else { - OnItemCreated(item, folder); + OnBaseItemCreated(item, folder); } }); } @@ -153,7 +154,7 @@ namespace MediaBrowser.Controller.Library } else { - OnItemCreated(item, parent); + OnBaseItemCreated(item, parent); } } @@ -299,28 +300,98 @@ namespace MediaBrowser.Controller.Library return returnFiles; } + /// + /// Gets a Person + /// public Person GetPerson(string name) { - // not yet implemented - return null; + string path = Path.Combine(ApplicationPaths.PeoplePath, name); + + return GetImagesByNameItem(path, name); } + /// + /// Gets a Studio + /// public Studio GetStudio(string name) { - // not yet implemented - return null; + string path = Path.Combine(ApplicationPaths.StudioPath, name); + + return GetImagesByNameItem(path, name); } + /// + /// Gets a Genre + /// public Genre GetGenre(string name) { - // not yet implemented - return null; + string path = Path.Combine(ApplicationPaths.GenrePath, name); + + return GetImagesByNameItem(path, name); } + /// + /// Gets a Year + /// public Year GetYear(int value) { - // not yet implemented - return null; + string path = Path.Combine(ApplicationPaths.YearPath, value.ToString()); + + return GetImagesByNameItem(path, value.ToString()); + } + + private Dictionary ImagesByNameItemCache = new Dictionary(); + + /// + /// Generically retrieves an IBN item + /// + private T GetImagesByNameItem(string path, string name) + where T : BaseEntity, new() + { + string key = path.ToLower(); + + // Look for it in the cache, if it's not there, create it + if (!ImagesByNameItemCache.ContainsKey(key)) + { + ImagesByNameItemCache[key] = CreateImagesByNameItem(path, name); + } + + return ImagesByNameItemCache[key] as T; + } + + /// + /// Creates an IBN item based on a given path + /// + private T CreateImagesByNameItem(string path, string name) + where T : BaseEntity, new () + { + T item = new T(); + + item.Name = name; + item.Id = Kernel.GetMD5(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"); + } + } + else + { + DateTime now = DateTime.Now; + + item.DateCreated = now; + item.DateModified = now; + } + + return item; } } }