From 374b7f2f039211d5a8fb80348b72070841f393ec Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 22 Apr 2013 00:38:03 -0400 Subject: [PATCH] new Artist entity --- MediaBrowser.Api/Images/ImageService.cs | 25 ++ MediaBrowser.Api/Library/LibraryService.cs | 137 +------ MediaBrowser.Api/MediaBrowser.Api.csproj | 1 + .../UserLibrary/ArtistsService.cs | 187 +++++++++ .../UserLibrary/BaseItemsByNameService.cs | 66 +++- .../UserLibrary/BaseItemsRequest.cs | 7 - MediaBrowser.Api/UserLibrary/GenresService.cs | 156 +++++--- .../UserLibrary/ItemByNameUserDataService.cs | 166 +++++--- MediaBrowser.Api/UserLibrary/ItemsService.cs | 9 +- .../UserLibrary/PersonsService.cs | 74 +++- .../UserLibrary/StudiosService.cs | 76 +++- MediaBrowser.Api/UserLibrary/YearsService.cs | 70 +++- MediaBrowser.Controller/Dto/DtoBuilder.cs | 2 +- .../Entities/Audio/Artist.cs | 2 +- .../Entities/Audio/Audio.cs | 10 + .../Entities/Audio/MusicAlbum.cs | 2 +- .../Entities/Audio/MusicArtist.cs | 8 - MediaBrowser.Controller/Entities/BaseItem.cs | 16 +- MediaBrowser.Controller/Entities/Genre.cs | 2 +- MediaBrowser.Controller/Entities/Person.cs | 2 +- MediaBrowser.Controller/Entities/Studio.cs | 2 +- MediaBrowser.Controller/Entities/Year.cs | 2 +- .../Library/ILibraryManager.cs | 8 + .../MediaBrowser.Controller.csproj | 9 + .../Providers/IProviderManager.cs | 3 +- .../Providers/Movies/FanArtMovieProvider.cs | 10 +- .../Providers/Movies/MovieDbProvider.cs | 11 +- .../Providers/Movies/TmdbPersonProvider.cs | 2 +- .../Providers/Music/FanArtAlbumProvider.cs | 112 ++++-- .../Music/FanArtArtistByNameProvider.cs | 48 +++ .../Providers/Music/FanArtArtistProvider.cs | 76 ++-- .../Providers/Music/LastfmAlbumProvider.cs | 85 ++-- .../Music/LastfmArtistByNameProvider.cs | 50 +++ .../Providers/Music/LastfmArtistProvider.cs | 21 +- .../Providers/Music/LastfmBaseProvider.cs | 79 +--- .../Providers/Music/LastfmHelper.cs | 20 +- .../Providers/TV/FanArtTVProvider.cs | 6 +- .../Providers/TV/RemoteEpisodeProvider.cs | 2 +- .../Providers/TV/RemoteSeasonProvider.cs | 8 +- .../Providers/TV/RemoteSeriesProvider.cs | 8 +- MediaBrowser.Controller/packages.config | 4 + .../Library/LibraryManager.cs | 108 +++++- ...MediaBrowser.Server.Implementations.csproj | 1 + .../Providers/ProviderManager.cs | 7 +- .../ScheduledTasks/ArtistValidationTask.cs | 81 ++++ .../ScheduledTasks/PeopleValidationTask.cs | 2 +- .../Sqlite/SQLiteRepository.cs | 4 +- .../Api/DashboardService.cs | 2 + MediaBrowser.WebDashboard/ApiClient.js | 366 +++++++++++++++--- .../MediaBrowser.WebDashboard.csproj | 12 + MediaBrowser.WebDashboard/packages.config | 2 +- 51 files changed, 1583 insertions(+), 586 deletions(-) create mode 100644 MediaBrowser.Api/UserLibrary/ArtistsService.cs create mode 100644 MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs create mode 100644 MediaBrowser.Controller/Providers/Music/LastfmArtistByNameProvider.cs create mode 100644 MediaBrowser.Controller/packages.config create mode 100644 MediaBrowser.Server.Implementations/ScheduledTasks/ArtistValidationTask.cs diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 1f69183cc8..19cb14bbd5 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -48,6 +48,19 @@ namespace MediaBrowser.Api.Images public string Name { get; set; } } + [Route("/Artists/{Name}/Images/{Type}", "GET")] + [Route("/Artists/{Name}/Images/{Type}/{Index}", "GET")] + [Api(Description = "Gets an artist image")] + public class GetArtistImage : ImageRequest + { + /// + /// Gets or sets the name. + /// + /// The name. + [ApiMember(Name = "Name", Description = "Artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Name { get; set; } + } + /// /// Class GetStudioImage /// @@ -233,6 +246,18 @@ namespace MediaBrowser.Api.Images return GetImage(request, item); } + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetArtistImage request) + { + var item = _libraryManager.GetArtist(request.Name).Result; + + return GetImage(request, item); + } + /// /// Gets the specified request. /// diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 5b34119dea..c7b4fae5f5 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -1,10 +1,6 @@ using MediaBrowser.Common; -using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; @@ -36,66 +32,6 @@ namespace MediaBrowser.Api.Library public bool HasInternetProvider { get; set; } } - /// - /// Class GetPerson - /// - [Route("/Persons/{Name}", "GET")] - [Api(Description = "Gets a person, by name")] - public class GetPerson : IReturn - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The person name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - - /// - /// Class GetStudio - /// - [Route("/Studios/{Name}", "GET")] - [Api(Description = "Gets a studio, by name")] - public class GetStudio : IReturn - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The studio name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - - /// - /// Class GetGenre - /// - [Route("/Genres/{Name}", "GET")] - [Api(Description = "Gets a genre, by name")] - public class GetGenre : IReturn - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - - /// - /// Class GetYear - /// - [Route("/Years/{Year}", "GET")] - [Api(Description = "Gets a year")] - public class GetYear : IReturn - { - /// - /// Gets or sets the year. - /// - /// The year. - [ApiMember(Name = "Year", Description = "The year", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] - public int Year { get; set; } - } - /// /// Class LibraryService /// @@ -106,16 +42,14 @@ namespace MediaBrowser.Api.Library /// private readonly IApplicationHost _appHost; private readonly ILibraryManager _libraryManager; - private readonly IUserDataRepository _userDataRepository; /// /// Initializes a new instance of the class. /// /// The app host. /// The library manager. - /// The user data repository. /// appHost - public LibraryService(IApplicationHost appHost, ILibraryManager libraryManager, IUserDataRepository userDataRepository) + public LibraryService(IApplicationHost appHost, ILibraryManager libraryManager) { if (appHost == null) { @@ -124,75 +58,6 @@ namespace MediaBrowser.Api.Library _appHost = appHost; _libraryManager = libraryManager; - _userDataRepository = userDataRepository; - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetPerson request) - { - var item = _libraryManager.GetPerson(request.Name).Result; - - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - - var result = new DtoBuilder(Logger, _libraryManager, _userDataRepository).GetBaseItemDto(item, fields.ToList()).Result; - - return ToOptimizedResult(result); - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetGenre request) - { - var item = _libraryManager.GetGenre(request.Name).Result; - - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - - var result = new DtoBuilder(Logger, _libraryManager, _userDataRepository).GetBaseItemDto(item, fields.ToList()).Result; - - return ToOptimizedResult(result); - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetStudio request) - { - var item = _libraryManager.GetStudio(request.Name).Result; - - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - - var result = new DtoBuilder(Logger, _libraryManager, _userDataRepository).GetBaseItemDto(item, fields.ToList()).Result; - - return ToOptimizedResult(result); - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetYear request) - { - var item = _libraryManager.GetYear(request.Year).Result; - - // Get everything - var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); - - var result = new DtoBuilder(Logger, _libraryManager, _userDataRepository).GetBaseItemDto(item, fields.ToList()).Result; - - return ToOptimizedResult(result); } /// diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 710c159e7f..32c1bfaf5c 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -87,6 +87,7 @@ + diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs new file mode 100644 index 0000000000..2622f0bd15 --- /dev/null +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -0,0 +1,187 @@ +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Querying; +using ServiceStack.ServiceHost; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediaBrowser.Api.UserLibrary +{ + /// + /// Class GetArtists + /// + [Route("/Artists", "GET")] + [Api(Description = "Gets all artists from a given item, folder, or the entire library")] + public class GetArtists : GetItemsByName + { + } + + /// + /// Class GetArtistsItemCounts + /// + [Route("/Artists/{Name}/Counts", "GET")] + [Api(Description = "Gets item counts of library items that an artist appears in")] + public class GetArtistsItemCounts : IReturn + { + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public Guid UserId { get; set; } + + /// + /// Gets or sets the name. + /// + /// The name. + [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Name { get; set; } + } + + [Route("/Artists/{Name}", "GET")] + [Api(Description = "Gets an artist, by name")] + public class GetArtist : IReturn + { + /// + /// Gets or sets the name. + /// + /// The name. + [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Name { get; set; } + + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } + } + + /// + /// Class ArtistsService + /// + public class ArtistsService : BaseItemsByNameService + { + /// + /// Initializes a new instance of the class. + /// + /// The user manager. + /// The library manager. + /// The user data repository. + public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository) + : base(userManager, libraryManager, userDataRepository) + { + } + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetArtist request) + { + var result = GetItem(request).Result; + + return ToOptimizedResult(result); + } + + /// + /// Gets the item. + /// + /// The request. + /// Task{BaseItemDto}. + private async Task GetItem(GetArtist request) + { + var item = await LibraryManager.GetArtist(request.Name).ConfigureAwait(false); + + // Get everything + var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + + if (request.UserId.HasValue) + { + var user = UserManager.GetUserById(request.UserId.Value); + + return await builder.GetBaseItemDto(item, user, fields.ToList()).ConfigureAwait(false); + } + + return await builder.GetBaseItemDto(item, fields.ToList()).ConfigureAwait(false); + } + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetArtistsItemCounts request) + { + var items = GetItems(request.UserId).OfType /// The request. /// The items. - /// The user. /// IEnumerable{BaseItem}. - private IEnumerable FilterItems(GetItemsByName request, IEnumerable items, User user) + private IEnumerable FilterItems(GetItemsByName request, IEnumerable items) { // Exclude item types if (!string.IsNullOrEmpty(request.ExcludeItemTypes)) @@ -213,9 +228,8 @@ namespace MediaBrowser.Api.UserLibrary /// /// The request. /// The items. - /// The user. /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. - protected abstract IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items, User user); + protected abstract IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items); /// /// Gets the dto. @@ -238,18 +252,36 @@ namespace MediaBrowser.Api.UserLibrary return null; } - var dto = await new DtoBuilder(Logger, LibraryManager, UserDataRepository).GetBaseItemDto(item, user, fields).ConfigureAwait(false); + var dto = user == null ? await new DtoBuilder(Logger, LibraryManager, UserDataRepository).GetBaseItemDto(item, fields).ConfigureAwait(false) : + await new DtoBuilder(Logger, LibraryManager, UserDataRepository).GetBaseItemDto(item, user, fields).ConfigureAwait(false); if (fields.Contains(ItemFields.ItemCounts)) { var items = stub.Items; dto.ChildCount = items.Count; - dto.RecentlyAddedItemCount = items.Count(i => i.IsRecentlyAdded(user)); + dto.RecentlyAddedItemCount = items.Count(i => i.IsRecentlyAdded()); } return dto; } + + /// + /// Gets the items. + /// + /// The user id. + /// IEnumerable{BaseItem}. + protected IEnumerable GetItems(Guid? userId) + { + if (userId.HasValue) + { + var user = UserManager.GetUserById(userId.Value); + + return UserManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); + } + + return LibraryManager.RootFolder.RecursiveChildren; + } } /// @@ -257,12 +289,24 @@ namespace MediaBrowser.Api.UserLibrary /// public class GetItemsByName : BaseItemsRequest, IReturn { + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } + /// /// What to sort the results by /// /// The sort by. [ApiMember(Name = "SortBy", Description = "Optional. Options: SortName", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string SortBy { get; set; } + + public GetItemsByName() + { + Recursive = true; + } } public class IbnStub diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index afe93a0865..7dcce53b2c 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -9,13 +9,6 @@ namespace MediaBrowser.Api.UserLibrary { public abstract class BaseItemsRequest { - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } - /// /// Skips over a given number of items within the results. Use for paging. /// diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 4f87b154e5..30094d4f32 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -1,11 +1,12 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; @@ -17,17 +18,13 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetGenres /// - [Route("/Users/{UserId}/Items/{ParentId}/Genres", "GET")] - [Route("/Users/{UserId}/Items/Root/Genres", "GET")] + [Route("/Genres", "GET")] [Api(Description = "Gets all genres from a given item, folder, or the entire library")] public class GetGenres : GetItemsByName { } - /// - /// Class GetGenreItemCounts - /// - [Route("/Users/{UserId}/Genres/{Name}/Counts", "GET")] + [Route("/Genres/{Name}/Counts", "GET")] [Api(Description = "Gets item counts of library items that a genre appears in")] public class GetGenreItemCounts : IReturn { @@ -35,8 +32,8 @@ namespace MediaBrowser.Api.UserLibrary /// Gets or sets the user id. /// /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } /// /// Gets or sets the name. @@ -45,6 +42,28 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Name { get; set; } } + + /// + /// Class GetGenre + /// + [Route("/Genres/{Name}", "GET")] + [Api(Description = "Gets a genre, by name")] + public class GetGenre : IReturn + { + /// + /// Gets or sets the name. + /// + /// The name. + [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Name { get; set; } + + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } + } /// /// Class GenresService @@ -56,6 +75,80 @@ namespace MediaBrowser.Api.UserLibrary { } + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetGenre request) + { + var result = GetItem(request).Result; + + return ToOptimizedResult(result); + } + + /// + /// Gets the item. + /// + /// The request. + /// Task{BaseItemDto}. + private async Task GetItem(GetGenre request) + { + var item = await LibraryManager.GetGenre(request.Name).ConfigureAwait(false); + + // Get everything + var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + + if (request.UserId.HasValue) + { + var user = UserManager.GetUserById(request.UserId.Value); + + return await builder.GetBaseItemDto(item, user, fields.ToList()).ConfigureAwait(false); + } + + return await builder.GetBaseItemDto(item, fields.ToList()).ConfigureAwait(false); + } + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetGenres request) + { + var result = GetResult(request).Result; + + return ToOptimizedResult(result); + } + + /// + /// Gets all items. + /// + /// The request. + /// The items. + /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. + protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items) + { + var itemsList = items.Where(i => i.Genres != null).ToList(); + + return itemsList + .SelectMany(i => i.Genres) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(name => new IbnStub(name, () => itemsList.Where(i => i.Genres.Contains(name, StringComparer.OrdinalIgnoreCase)), GetEntity)); + } + + /// + /// Gets the entity. + /// + /// The name. + /// Task{Genre}. + protected Task GetEntity(string name) + { + return LibraryManager.GetGenre(name); + } + /// /// Gets the specified request. /// @@ -63,9 +156,7 @@ namespace MediaBrowser.Api.UserLibrary /// System.Object. public object Get(GetGenreItemCounts request) { - var user = UserManager.GetUserById(request.UserId); - - var items = user.RootFolder.GetRecursiveChildren(user).Where(i => i.Genres != null && i.Genres.Contains(request.Name, StringComparer.OrdinalIgnoreCase)).ToList(); + var items = GetItems(request.UserId).Where(i => i.Genres != null && i.Genres.Contains(request.Name, StringComparer.OrdinalIgnoreCase)).ToList(); var counts = new ItemByNameCounts { @@ -86,44 +177,5 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(counts); } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetGenres request) - { - var result = GetResult(request).Result; - - return ToOptimizedResult(result); - } - - /// - /// Gets all items. - /// - /// The request. - /// The items. - /// The user. - /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. - protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items, User user) - { - var itemsList = items.Where(i => i.Genres != null).ToList(); - - return itemsList - .SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) - .Select(name => new IbnStub(name, () => itemsList.Where(i => i.Genres.Contains(name, StringComparer.OrdinalIgnoreCase)), GetEntity)); - } - - /// - /// Gets the entity. - /// - /// The name. - /// Task{Genre}. - protected Task GetEntity(string name) - { - return LibraryManager.GetGenre(name); - } } } diff --git a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs index e068eb2163..a3f36afe9d 100644 --- a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs @@ -1,38 +1,21 @@ -using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using ServiceStack.ServiceHost; +using ServiceStack.Text.Controller; using System; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Api.UserLibrary { - /// - /// Class GetItemByNameUserData - /// - [Route("/Users/{UserId}/ItemsByName/{Name}/UserData", "GET")] - [Api(Description = "Gets user data for an item")] - public class GetItemByNameUserData : IReturnVoid - { - /// - /// Gets or sets the user id. - /// - /// The user id. - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist, album)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - /// /// Class MarkItemByNameFavorite /// - [Route("/Users/{UserId}/ItemsByName/Favorites/{Name}", "POST")] + [Route("/Users/{UserId}/Favorites/Artists/{Name}", "POST")] + [Route("/Users/{UserId}/Favorites/Persons/{Name}", "POST")] + [Route("/Users/{UserId}/Favorites/Studios/{Name}", "POST")] + [Route("/Users/{UserId}/Favorites/Genres/{Name}", "POST")] [Api(Description = "Marks something as a favorite")] public class MarkItemByNameFavorite : IReturnVoid { @@ -47,14 +30,17 @@ namespace MediaBrowser.Api.UserLibrary /// Gets or sets the name. /// /// The name. - [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist, album)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + [ApiMember(Name = "Name", Description = "The name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Name { get; set; } } /// /// Class UnmarkItemByNameFavorite /// - [Route("/Users/{UserId}/ItemsByName/Favorites/{Name}", "DELETE")] + [Route("/Users/{UserId}/Favorites/Artists/{Name}", "DELETE")] + [Route("/Users/{UserId}/Favorites/Persons/{Name}", "DELETE")] + [Route("/Users/{UserId}/Favorites/Studios/{Name}", "DELETE")] + [Route("/Users/{UserId}/Favorites/Genres/{Name}", "DELETE")] [Api(Description = "Unmarks something as a favorite")] public class UnmarkItemByNameFavorite : IReturnVoid { @@ -69,11 +55,17 @@ namespace MediaBrowser.Api.UserLibrary /// Gets or sets the name. /// /// The name. - [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist, album)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + [ApiMember(Name = "Name", Description = "The name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Name { get; set; } } - [Route("/Users/{UserId}/ItemsByName/{Name}/Rating", "POST")] + /// + /// Class UpdateItemByNameRating + /// + [Route("/Users/{UserId}/Ratings/Artists/{Name}", "POST")] + [Route("/Users/{UserId}/Ratings/Persons/{Name}", "POST")] + [Route("/Users/{UserId}/Ratings/Studios/{Name}", "POST")] + [Route("/Users/{UserId}/Ratings/Genres/{Name}", "POST")] [Api(Description = "Updates a user's rating for an item")] public class UpdateItemByNameRating : IReturnVoid { @@ -88,7 +80,7 @@ namespace MediaBrowser.Api.UserLibrary /// Gets or sets the name. /// /// The name. - [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist, album)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + [ApiMember(Name = "Name", Description = "The name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Name { get; set; } /// @@ -99,7 +91,13 @@ namespace MediaBrowser.Api.UserLibrary public bool Likes { get; set; } } - [Route("/Users/{UserId}/ItemsByName/{Name}/Rating", "DELETE")] + /// + /// Class DeleteItemByNameRating + /// + [Route("/Users/{UserId}/Ratings/Artists/{Name}", "DELETE")] + [Route("/Users/{UserId}/Ratings/Persons/{Name}", "DELETE")] + [Route("/Users/{UserId}/Ratings/Studios/{Name}", "DELETE")] + [Route("/Users/{UserId}/Ratings/Genres/{Name}", "DELETE")] [Api(Description = "Deletes a user's saved personal rating for an item")] public class DeleteItemByNameRating : IReturnVoid { @@ -114,10 +112,10 @@ namespace MediaBrowser.Api.UserLibrary /// Gets or sets the name. /// /// The name. - [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist, album)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + [ApiMember(Name = "Name", Description = "The item name (genre, person, year, studio, artist)", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Name { get; set; } } - + /// /// Class ItemByNameUserDataService /// @@ -128,35 +126,32 @@ namespace MediaBrowser.Api.UserLibrary /// protected readonly IUserDataRepository UserDataRepository; + /// + /// The library manager + /// + protected readonly ILibraryManager LibraryManager; + /// /// Initializes a new instance of the class. /// /// The user data repository. - public ItemByNameUserDataService(IUserDataRepository userDataRepository) + /// The library manager. + public ItemByNameUserDataService(IUserDataRepository userDataRepository, ILibraryManager libraryManager) { UserDataRepository = userDataRepository; + LibraryManager = libraryManager; } - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetItemByNameUserData request) - { - // Get the user data for this item - var data = UserDataRepository.GetUserData(request.UserId, request.Name).Result; - - return ToOptimizedResult(DtoBuilder.GetUserItemDataDto(data)); - } - /// /// Posts the specified request. /// /// The request. public void Post(MarkItemByNameFavorite request) { - var task = MarkFavorite(request.UserId, request.Name, true); + var pathInfo = PathInfo.Parse(RequestContext.PathInfo); + var type = pathInfo.GetArgumentValue(3); + + var task = MarkFavorite(request.UserId, type, request.Name, true); Task.WaitAll(task); } @@ -167,18 +162,24 @@ namespace MediaBrowser.Api.UserLibrary /// The request. public void Post(UpdateItemByNameRating request) { - var task = MarkLike(request.UserId, request.Name, request.Likes); + var pathInfo = PathInfo.Parse(RequestContext.PathInfo); + var type = pathInfo.GetArgumentValue(3); + + var task = MarkLike(request.UserId, type, request.Name, request.Likes); Task.WaitAll(task); } - + /// /// Deletes the specified request. /// /// The request. public void Delete(UnmarkItemByNameFavorite request) { - var task = MarkFavorite(request.UserId, request.Name, false); + var pathInfo = PathInfo.Parse(RequestContext.PathInfo); + var type = pathInfo.GetArgumentValue(3); + + var task = MarkFavorite(request.UserId, type, request.Name, false); Task.WaitAll(task); } @@ -189,7 +190,10 @@ namespace MediaBrowser.Api.UserLibrary /// The request. public void Delete(DeleteItemByNameRating request) { - var task = MarkLike(request.UserId, request.Name, null); + var pathInfo = PathInfo.Parse(RequestContext.PathInfo); + var type = pathInfo.GetArgumentValue(3); + + var task = MarkLike(request.UserId, type, request.Name, null); Task.WaitAll(task); } @@ -198,11 +202,37 @@ namespace MediaBrowser.Api.UserLibrary /// Marks the favorite. /// /// The user id. - /// The key. + /// The type. + /// The name. /// if set to true [is favorite]. /// Task. - protected async Task MarkFavorite(Guid userId, string key, bool isFavorite) + protected async Task MarkFavorite(Guid userId, string type, string name, bool isFavorite) { + BaseItem item; + + if (string.Equals(type, "Persons")) + { + item = await LibraryManager.GetPerson(name).ConfigureAwait(false); + } + else if (string.Equals(type, "Artists")) + { + item = await LibraryManager.GetArtist(name).ConfigureAwait(false); + } + else if (string.Equals(type, "Genres")) + { + item = await LibraryManager.GetGenre(name).ConfigureAwait(false); + } + else if (string.Equals(type, "Studios")) + { + item = await LibraryManager.GetStudio(name).ConfigureAwait(false); + } + else + { + throw new ArgumentException(); + } + + var key = item.GetUserDataKey(); + // Get the user data for this item var data = await UserDataRepository.GetUserData(userId, key).ConfigureAwait(false); @@ -216,11 +246,37 @@ namespace MediaBrowser.Api.UserLibrary /// Marks the like. /// /// The user id. - /// The key. + /// The type. + /// The name. /// if set to true [likes]. /// Task. - protected async Task MarkLike(Guid userId, string key, bool? likes) + protected async Task MarkLike(Guid userId, string type, string name, bool? likes) { + BaseItem item; + + if (string.Equals(type, "Persons")) + { + item = await LibraryManager.GetPerson(name).ConfigureAwait(false); + } + else if (string.Equals(type, "Artists")) + { + item = await LibraryManager.GetArtist(name).ConfigureAwait(false); + } + else if (string.Equals(type, "Genres")) + { + item = await LibraryManager.GetGenre(name).ConfigureAwait(false); + } + else if (string.Equals(type, "Studios")) + { + item = await LibraryManager.GetStudio(name).ConfigureAwait(false); + } + else + { + throw new ArgumentException(); + } + + var key = item.GetUserDataKey(); + // Get the user data for this item var data = await UserDataRepository.GetUserData(userId, key).ConfigureAwait(false); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 79e00998f5..7d3581846d 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -21,6 +21,13 @@ namespace MediaBrowser.Api.UserLibrary [Api(Description = "Gets items based on a query.")] public class GetItems : BaseItemsRequest, IReturn { + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public Guid UserId { get; set; } + /// /// Limit results to items containing a specific person /// @@ -328,7 +335,7 @@ namespace MediaBrowser.Api.UserLibrary }); case ItemFilter.IsRecentlyAdded: - return items.Where(item => item.IsRecentlyAdded(currentUser)); + return items.Where(item => item.IsRecentlyAdded()); case ItemFilter.IsResumable: return items.Where(item => diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index fb623e953e..ee16a986e2 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; @@ -6,6 +7,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; @@ -17,8 +19,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetPersons /// - [Route("/Users/{UserId}/Items/{ParentId}/Persons", "GET")] - [Route("/Users/{UserId}/Items/Root/Persons", "GET")] + [Route("/Persons", "GET")] [Api(Description = "Gets all persons from a given item, folder, or the entire library")] public class GetPersons : GetItemsByName { @@ -32,7 +33,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetPersonItemCounts /// - [Route("/Users/{UserId}/Persons/{Name}/Counts", "GET")] + [Route("/Persons/{Name}/Counts", "GET")] [Api(Description = "Gets item counts of library items that a person appears in")] public class GetPersonItemCounts : IReturn { @@ -51,6 +52,28 @@ namespace MediaBrowser.Api.UserLibrary public string Name { get; set; } } + /// + /// Class GetPerson + /// + [Route("/Persons/{Name}", "GET")] + [Api(Description = "Gets a person, by name")] + public class GetPerson : IReturn + { + /// + /// Gets or sets the name. + /// + /// The name. + [ApiMember(Name = "Name", Description = "The person name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Name { get; set; } + + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } + } + /// /// Class PersonsService /// @@ -67,6 +90,42 @@ namespace MediaBrowser.Api.UserLibrary { } + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetPerson request) + { + var result = GetItem(request).Result; + + return ToOptimizedResult(result); + } + + /// + /// Gets the item. + /// + /// The request. + /// Task{BaseItemDto}. + private async Task GetItem(GetPerson request) + { + var item = await LibraryManager.GetPerson(request.Name).ConfigureAwait(false); + + // Get everything + var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + + if (request.UserId.HasValue) + { + var user = UserManager.GetUserById(request.UserId.Value); + + return await builder.GetBaseItemDto(item, user, fields.ToList()).ConfigureAwait(false); + } + + return await builder.GetBaseItemDto(item, fields.ToList()).ConfigureAwait(false); + } + /// /// Gets the specified request. /// @@ -86,9 +145,7 @@ namespace MediaBrowser.Api.UserLibrary /// System.Object. public object Get(GetPersonItemCounts request) { - var user = UserManager.GetUserById(request.UserId); - - var items = user.RootFolder.GetRecursiveChildren(user).Where(i => i.People != null && i.People.Any(p => string.Equals(p.Name, request.Name, StringComparison.OrdinalIgnoreCase))).ToList(); + var items = GetItems(request.UserId).Where(i => i.People != null && i.People.Any(p => string.Equals(p.Name, request.Name, StringComparison.OrdinalIgnoreCase))).ToList(); var counts = new ItemByNameCounts { @@ -117,9 +174,8 @@ namespace MediaBrowser.Api.UserLibrary /// /// The request. /// The items. - /// The user. /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. - protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items, User user) + protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items) { var inputPersonTypes = ((GetPersons)request).PersonTypes; var personTypes = string.IsNullOrEmpty(inputPersonTypes) ? new string[] { } : inputPersonTypes.Split(','); diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index 94c6046854..4e3a2d42aa 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -1,10 +1,12 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; @@ -16,14 +18,13 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetStudios /// - [Route("/Users/{UserId}/Items/{ParentId}/Studios", "GET")] - [Route("/Users/{UserId}/Items/Root/Studios", "GET")] + [Route("/Studios", "GET")] [Api(Description = "Gets all studios from a given item, folder, or the entire library")] public class GetStudios : GetItemsByName { } - [Route("/Users/{UserId}/Studios/{Name}/Counts", "GET")] + [Route("/Studios/{Name}/Counts", "GET")] [Api(Description = "Gets item counts of library items that a studio appears in")] public class GetStudioItemCounts : IReturn { @@ -42,6 +43,28 @@ namespace MediaBrowser.Api.UserLibrary public string Name { get; set; } } + /// + /// Class GetStudio + /// + [Route("/Studios/{Name}", "GET")] + [Api(Description = "Gets a studio, by name")] + public class GetStudio : IReturn + { + /// + /// Gets or sets the name. + /// + /// The name. + [ApiMember(Name = "Name", Description = "The studio name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Name { get; set; } + + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } + } + /// /// Class StudiosService /// @@ -52,6 +75,42 @@ namespace MediaBrowser.Api.UserLibrary { } + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetStudio request) + { + var result = GetItem(request).Result; + + return ToOptimizedResult(result); + } + + /// + /// Gets the item. + /// + /// The request. + /// Task{BaseItemDto}. + private async Task GetItem(GetStudio request) + { + var item = await LibraryManager.GetStudio(request.Name).ConfigureAwait(false); + + // Get everything + var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + + if (request.UserId.HasValue) + { + var user = UserManager.GetUserById(request.UserId.Value); + + return await builder.GetBaseItemDto(item, user, fields.ToList()).ConfigureAwait(false); + } + + return await builder.GetBaseItemDto(item, fields.ToList()).ConfigureAwait(false); + } + /// /// Gets the specified request. /// @@ -59,9 +118,7 @@ namespace MediaBrowser.Api.UserLibrary /// System.Object. public object Get(GetStudioItemCounts request) { - var user = UserManager.GetUserById(request.UserId); - - var items = user.RootFolder.GetRecursiveChildren(user).Where(i => i.Studios != null && i.Studios.Contains(request.Name, StringComparer.OrdinalIgnoreCase)).ToList(); + var items = GetItems(request.UserId).Where(i => i.Studios != null && i.Studios.Contains(request.Name, StringComparer.OrdinalIgnoreCase)).ToList(); var counts = new ItemByNameCounts { @@ -94,15 +151,14 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(result); } - + /// /// Gets all items. /// /// The request. /// The items. - /// The user. /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. - protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items, User user) + protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items) { var itemsList = items.Where(i => i.Studios != null).ToList(); diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index b22a8dac35..79cb08ef92 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -1,7 +1,11 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -12,13 +16,34 @@ namespace MediaBrowser.Api.UserLibrary /// /// Class GetYears /// - [Route("/Users/{UserId}/Items/{ParentId}/Years", "GET")] - [Route("/Users/{UserId}/Items/Root/Years", "GET")] + [Route("/Years", "GET")] [Api(Description = "Gets all years from a given item, folder, or the entire library")] public class GetYears : GetItemsByName { } + /// + /// Class GetYear + /// + [Route("/Years/{Year}", "GET")] + [Api(Description = "Gets a year")] + public class GetYear : IReturn + { + /// + /// Gets or sets the year. + /// + /// The year. + [ApiMember(Name = "Year", Description = "The year", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] + public int Year { get; set; } + + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid? UserId { get; set; } + } + /// /// Class YearsService /// @@ -34,6 +59,42 @@ namespace MediaBrowser.Api.UserLibrary { } + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetYear request) + { + var result = GetItem(request).Result; + + return ToOptimizedResult(result); + } + + /// + /// Gets the item. + /// + /// The request. + /// Task{BaseItemDto}. + private async Task GetItem(GetYear request) + { + var item = await LibraryManager.GetYear(request.Year).ConfigureAwait(false); + + // Get everything + var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)); + + var builder = new DtoBuilder(Logger, LibraryManager, UserDataRepository); + + if (request.UserId.HasValue) + { + var user = UserManager.GetUserById(request.UserId.Value); + + return await builder.GetBaseItemDto(item, user, fields.ToList()).ConfigureAwait(false); + } + + return await builder.GetBaseItemDto(item, fields.ToList()).ConfigureAwait(false); + } + /// /// Gets the specified request. /// @@ -51,9 +112,8 @@ namespace MediaBrowser.Api.UserLibrary /// /// The request. /// The items. - /// The user. /// IEnumerable{Tuple{System.StringFunc{System.Int32}}}. - protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items, User user) + protected override IEnumerable> GetAllItems(GetItemsByName request, IEnumerable items) { var itemsList = items.Where(i => i.ProductionYear != null).ToList(); diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs index 2631488a58..371241d1ba 100644 --- a/MediaBrowser.Controller/Dto/DtoBuilder.cs +++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs @@ -528,7 +528,7 @@ namespace MediaBrowser.Controller.Dto recursiveItemCount++; // Check is recently added - if (child.IsRecentlyAdded(user)) + if (child.IsRecentlyAdded()) { rcentlyAddedItemCount++; } diff --git a/MediaBrowser.Controller/Entities/Audio/Artist.cs b/MediaBrowser.Controller/Entities/Audio/Artist.cs index dcd6af92d3..567b678685 100644 --- a/MediaBrowser.Controller/Entities/Audio/Artist.cs +++ b/MediaBrowser.Controller/Entities/Audio/Artist.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities.Audio /// System.String. public override string GetUserDataKey() { - return Name; + return "Artist-" + Name; } } } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 9deb8241de..01bdd84aca 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -113,5 +113,15 @@ namespace MediaBrowser.Controller.Entities.Audio return (ProductionYear != null ? ProductionYear.Value.ToString("000-") : "") + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; } + + /// + /// Determines whether the specified name has artist. + /// + /// The name. + /// true if the specified name has artist; otherwise, false. + public bool HasArtist(string name) + { + return Artists.Contains(name, StringComparer.OrdinalIgnoreCase) || string.Equals(AlbumArtist, name, StringComparison.OrdinalIgnoreCase); + } } } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 7b64c0e854..7d6577b4ec 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Entities.Audio /// /// The unknwon artist /// - private static readonly MusicArtist UnknwonArtist = new MusicArtist {Name = ""}; + private static readonly MusicArtist UnknwonArtist = new MusicArtist { Name = "" }; /// /// Override this to return the folder that should be used to construct a container diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index b5627e061f..1f1d5e0837 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -1,6 +1,4 @@  -using System.Collections.Generic; - namespace MediaBrowser.Controller.Entities.Audio { /// @@ -8,12 +6,6 @@ namespace MediaBrowser.Controller.Entities.Audio /// public class MusicArtist : Folder { - public Dictionary AlbumCovers { get; set; } - public override void ClearMetaValues() - { - AlbumCovers = null; - base.ClearMetaValues(); - } } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f7963c6e69..1d803ea455 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -23,6 +23,14 @@ namespace MediaBrowser.Controller.Entities /// public abstract class BaseItem : IHasProviderIds { + protected BaseItem() + { + Genres = new List(); + TrailerUrls = new List(); + Studios = new List(); + People = new List(); + } + /// /// The trailer folder name /// @@ -925,16 +933,10 @@ namespace MediaBrowser.Controller.Entities /// /// Determines if the item is considered new based on user settings /// - /// The user. /// true if [is recently added] [the specified user]; otherwise, false. /// - public bool IsRecentlyAdded(User user) + public bool IsRecentlyAdded() { - if (user == null) - { - throw new ArgumentNullException(); - } - return (DateTime.UtcNow - DateCreated).TotalDays < ConfigurationManager.Configuration.RecentItemDays; } diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 619c7a12b6..b2b4653532 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities /// System.String. public override string GetUserDataKey() { - return Name; + return "Genre-" + Name; } } } diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index f5570448da..0f2803744e 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities /// System.String. public override string GetUserDataKey() { - return Name; + return "Person-" + Name; } } diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 06511d959e..0dec024f86 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities /// System.String. public override string GetUserDataKey() { - return Name; + return "Studio-" + Name; } } } diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index 1e4e6cb06b..307ce306bf 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities /// System.String. public override string GetUserDataKey() { - return Name; + return "Year-" + Name; } } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 86fd25e661..060f52e37e 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -213,5 +213,13 @@ namespace MediaBrowser.Controller.Library /// The parent. /// IEnumerable{BaseItem}. IEnumerable RetrieveChildren(Folder parent); + + /// + /// Validates the artists. + /// + /// The cancellation token. + /// The progress. + /// Task. + Task ValidateArtists(CancellationToken cancellationToken, IProgress progress); } } \ No newline at end of file diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 926639d0c7..09a59286ee 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -53,8 +53,12 @@ MinimumRecommendedRules.ruleset + + ..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll + + @@ -111,9 +115,11 @@ + + @@ -197,6 +203,9 @@ MediaBrowser.Model + + + if $(ConfigurationName) == Release ( diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index cb7237a9df..fa10c85855 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -14,11 +14,12 @@ namespace MediaBrowser.Controller.Providers /// The item. /// The source. /// Name of the target. + /// if set to true [save locally]. /// The resource pool. /// The cancellation token. /// Task{System.String}. /// item - Task DownloadAndSaveImage(BaseItem item, string source, string targetName, SemaphoreSlim resourcePool, CancellationToken cancellationToken); + Task DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally, SemaphoreSlim resourcePool, CancellationToken cancellationToken); /// /// Saves to library filesystem. diff --git a/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs index 94fe386806..b6155f6129 100644 --- a/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs @@ -150,7 +150,7 @@ namespace MediaBrowser.Controller.Providers.Movies Logger.Debug("FanArtProvider getting ClearLogo for " + movie.Name); try { - movie.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(movie, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + movie.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(movie, path, LOGO_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -176,7 +176,7 @@ namespace MediaBrowser.Controller.Providers.Movies Logger.Debug("FanArtProvider getting ClearArt for " + movie.Name); try { - movie.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(movie, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + movie.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(movie, path, ART_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -199,7 +199,7 @@ namespace MediaBrowser.Controller.Providers.Movies Logger.Debug("FanArtProvider getting DiscArt for " + movie.Name); try { - movie.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(movie, path, DISC_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + movie.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(movie, path, DISC_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -223,7 +223,7 @@ namespace MediaBrowser.Controller.Providers.Movies Logger.Debug("FanArtProvider getting Banner for " + movie.Name); try { - movie.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(movie, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + movie.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(movie, path, BANNER_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -247,7 +247,7 @@ namespace MediaBrowser.Controller.Providers.Movies Logger.Debug("FanArtProvider getting Banner for " + movie.Name); try { - movie.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(movie, path, THUMB_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + movie.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(movie, path, THUMB_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { diff --git a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs index a013a85e01..0defafe419 100644 --- a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs @@ -251,7 +251,7 @@ namespace MediaBrowser.Controller.Providers.Movies new Regex(@"(?.*)") // last resort matches the whole string as the name }; - public const string LOCAL_META_FILE_NAME = "MBMovie.json"; + public const string LOCAL_META_FILE_NAME = "mbmovie.js"; public const string ALT_META_FILE_NAME = "movie.xml"; protected string ItemType = "movie"; @@ -268,7 +268,7 @@ namespace MediaBrowser.Controller.Providers.Movies } - if (providerInfo.LastRefreshStatus == ProviderRefreshStatus.CompletedWithErrors) + if (providerInfo.LastRefreshStatus != ProviderRefreshStatus.Success) { Logger.Debug("MovieProvider for {0} - last attempt had errors. Will try again.", item.Path); return true; @@ -281,9 +281,6 @@ namespace MediaBrowser.Controller.Providers.Movies return false; } - if (DateTime.Today.Subtract(item.DateCreated).TotalDays > 180 && downloadDate != DateTime.MinValue) - return false; // don't trigger a refresh data for item that are more than 6 months old and have been refreshed before - if (DateTime.Today.Subtract(downloadDate).TotalDays < ConfigurationManager.Configuration.MetadataRefreshDays) // only refresh every n days return false; @@ -1034,7 +1031,7 @@ namespace MediaBrowser.Controller.Providers.Movies { try { - item.PrimaryImagePath = await ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + poster.file_path, "folder" + Path.GetExtension(poster.file_path), MovieDbResourcePool, cancellationToken).ConfigureAwait(false); + item.PrimaryImagePath = await ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + poster.file_path, "folder" + Path.GetExtension(poster.file_path), ConfigurationManager.Configuration.SaveLocalMeta, MovieDbResourcePool, cancellationToken).ConfigureAwait(false); } catch (HttpException) { @@ -1066,7 +1063,7 @@ namespace MediaBrowser.Controller.Providers.Movies { try { - item.BackdropImagePaths.Add(await ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + images.backdrops[i].file_path, bdName + Path.GetExtension(images.backdrops[i].file_path), MovieDbResourcePool, cancellationToken).ConfigureAwait(false)); + item.BackdropImagePaths.Add(await ProviderManager.DownloadAndSaveImage(item, tmdbImageUrl + images.backdrops[i].file_path, bdName + Path.GetExtension(images.backdrops[i].file_path), ConfigurationManager.Configuration.SaveLocalMeta, MovieDbResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { diff --git a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs index 583e0bb97b..affd4757a3 100644 --- a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Providers.Movies /// /// The meta file name /// - protected const string MetaFileName = "MBPerson.json"; + protected const string MetaFileName = "mbperson.js"; protected readonly IProviderManager ProviderManager; diff --git a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs index f850722d1a..4d7f78413d 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs @@ -1,29 +1,34 @@ -using System.Collections.Generic; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; using System; +using System.IO; using System.Threading; using System.Threading.Tasks; +using System.Xml; namespace MediaBrowser.Controller.Providers.Music { public class FanArtAlbumProvider : FanartBaseProvider { private readonly IProviderManager _providerManager; - - public FanArtAlbumProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + + protected IHttpClient HttpClient { get; private set; } + + public FanArtAlbumProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) : base(logManager, configurationManager) { _providerManager = providerManager; + HttpClient = httpClient; } public override bool Supports(BaseItem item) { - return item is MusicAlbum && item.Parent is MusicArtist; + return item is MusicAlbum; } /// @@ -35,7 +40,7 @@ namespace MediaBrowser.Controller.Providers.Music protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { //we fetch if image needed and haven't already tried recently - return string.IsNullOrEmpty(item.PrimaryImagePath) && + return (string.IsNullOrEmpty(item.PrimaryImagePath) || !item.HasImage(ImageType.Disc)) && DateTime.Today.Subtract(providerInfo.LastRefreshed).TotalDays > ConfigurationManager.Configuration.MetadataRefreshDays; } @@ -45,46 +50,81 @@ namespace MediaBrowser.Controller.Providers.Music if (mbid == null) { Logger.Warn("No Musicbrainz id associated with album {0}", item.Name); - SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.CompletedWithErrors); - return false; + SetLastRefreshed(item, DateTime.UtcNow); + return true; } cancellationToken.ThrowIfCancellationRequested(); - //Look at our parent for our album cover - var artist = (MusicArtist)item.Parent; + var url = string.Format("http://api.fanart.tv/webservice/album/{0}/{1}/xml/all/1/1", APIKey, item.GetProviderId(MetadataProviders.Musicbrainz)); - var cover = artist.AlbumCovers != null ? GetValueOrDefault(artist.AlbumCovers, mbid, null) : null; + var doc = new XmlDocument(); - if (cover == null) + try + { + using (var xml = await HttpClient.Get(url, FanArtResourcePool, cancellationToken).ConfigureAwait(false)) + { + doc.Load(xml); + } + } + catch (HttpException) { - Logger.Warn("Unable to find cover art for {0}", item.Name); - SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.CompletedWithErrors); - return false; } - item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, cover, "folder.jpg", FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + cancellationToken.ThrowIfCancellationRequested(); + + if (doc.HasChildNodes) + { + if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.ResolveArgs.ContainsMetaFileByName(DISC_FILE)) + { + var node = doc.SelectSingleNode("//fanart/music/albums/album//cdart/@url"); + + var path = node != null ? node.Value : null; + + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting Disc for " + item.Name); + try + { + item.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(item, path, DISC_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + + if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.ResolveArgs.ContainsMetaFileByName(PRIMARY_FILE)) + { + var node = doc.SelectSingleNode("//fanart/music/albums/album//albumcover/@url"); + + var path = node != null ? node.Value : null; + + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting albumcover for " + item.Name); + try + { + item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, path, PRIMARY_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + } + + SetLastRefreshed(item, DateTime.UtcNow); + return true; } - - /// - /// Helper method for Dictionaries since they throw on not-found keys - /// - /// - /// - /// The dictionary. - /// The key. - /// The default value. - /// ``1. - private static U GetValueOrDefault(Dictionary dictionary, T key, U defaultValue) - { - U val; - if (!dictionary.TryGetValue(key, out val)) - { - val = defaultValue; - } - return val; - - } } } diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs new file mode 100644 index 0000000000..58200a458c --- /dev/null +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs @@ -0,0 +1,48 @@ +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Controller.Providers.Music +{ + /// + /// Class FanArtArtistByNameProvider + /// + public class FanArtArtistByNameProvider : FanArtArtistProvider + { + /// + /// Initializes a new instance of the class. + /// + /// The HTTP client. + /// The log manager. + /// The configuration manager. + /// The provider manager. + public FanArtArtistByNameProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + : base(httpClient, logManager, configurationManager, providerManager) + { + } + + /// + /// Supportses the specified item. + /// + /// The item. + /// true if XXXX, false otherwise + public override bool Supports(BaseItem item) + { + return item is Artist; + } + + /// + /// Gets a value indicating whether [save local meta]. + /// + /// true if [save local meta]; otherwise, false. + protected override bool SaveLocalMeta + { + get + { + return true; + } + } + } +} diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs index 1dd5c7cd25..ec99a78a7b 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Providers.Music /// /// Class FanArtArtistProvider /// - class FanArtArtistProvider : FanartBaseProvider + public class FanArtArtistProvider : FanartBaseProvider { /// /// Gets the HTTP client. @@ -54,6 +54,11 @@ namespace MediaBrowser.Controller.Providers.Music return item is MusicArtist; } + protected virtual bool SaveLocalMeta + { + get { return ConfigurationManager.Configuration.SaveLocalMeta; } + } + /// /// Shoulds the fetch. /// @@ -62,16 +67,11 @@ namespace MediaBrowser.Controller.Providers.Music /// true if XXXX, false otherwise protected override bool ShouldFetch(BaseItem item, BaseProviderInfo providerInfo) { - var artist = (MusicArtist)item; - if (item.Path == null || item.DontFetchMeta || string.IsNullOrEmpty(artist.GetProviderId(MetadataProviders.Musicbrainz))) return false; //nothing to do - var artExists = item.ResolveArgs.ContainsMetaFileByName(ART_FILE); - var logoExists = item.ResolveArgs.ContainsMetaFileByName(LOGO_FILE); - var discExists = item.ResolveArgs.ContainsMetaFileByName(DISC_FILE); + if (item.Path == null || item.DontFetchMeta || string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz))) return false; //nothing to do - return (!artExists && ConfigurationManager.Configuration.DownloadMusicArtistImages.Art) - || (!logoExists && ConfigurationManager.Configuration.DownloadMusicArtistImages.Logo) - || (!discExists && ConfigurationManager.Configuration.DownloadMusicArtistImages.Disc) - || ((artist.AlbumCovers == null || artist.AlbumCovers.Count == 0) && ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary); + return (!item.ResolveArgs.ContainsMetaFileByName(ART_FILE) && ConfigurationManager.Configuration.DownloadMusicArtistImages.Art) + || (!item.ResolveArgs.ContainsMetaFileByName(LOGO_FILE) && ConfigurationManager.Configuration.DownloadMusicArtistImages.Logo) + || (!item.ResolveArgs.ContainsMetaFileByName(DISC_FILE) && ConfigurationManager.Configuration.DownloadMusicArtistImages.Disc); } /// @@ -85,7 +85,7 @@ namespace MediaBrowser.Controller.Providers.Music { cancellationToken.ThrowIfCancellationRequested(); - var artist = (MusicArtist)item; + //var artist = item; BaseProviderInfo providerData; @@ -94,9 +94,9 @@ namespace MediaBrowser.Controller.Providers.Music providerData = new BaseProviderInfo(); } - if (ShouldFetch(artist, providerData)) + if (ShouldFetch(item, providerData)) { - var url = string.Format(FanArtBaseUrl, APIKey, artist.GetProviderId(MetadataProviders.Musicbrainz)); + var url = string.Format(FanArtBaseUrl, APIKey, item.GetProviderId(MetadataProviders.Musicbrainz)); var doc = new XmlDocument(); try @@ -124,10 +124,10 @@ namespace MediaBrowser.Controller.Providers.Music path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting ClearLogo for " + artist.Name); + Logger.Debug("FanArtProvider getting ClearLogo for " + item.Name); try { - artist.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(artist, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LOGO_FILE, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -146,16 +146,16 @@ namespace MediaBrowser.Controller.Providers.Music if (nodes != null) { var numBackdrops = 0; - artist.BackdropImagePaths = new List(); + item.BackdropImagePaths = new List(); foreach (XmlNode node in nodes) { path = node.Value; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting Backdrop for " + artist.Name); + Logger.Debug("FanArtProvider getting Backdrop for " + item.Name); try { - artist.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(artist, path, ("Backdrop" + (numBackdrops > 0 ? numBackdrops.ToString() : "") + ".jpg"), FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("Backdrop" + (numBackdrops > 0 ? numBackdrops.ToString() : "") + ".jpg"), SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); numBackdrops++; if (numBackdrops >= ConfigurationManager.Configuration.MaxBackdrops) break; } @@ -175,32 +175,6 @@ namespace MediaBrowser.Controller.Providers.Music cancellationToken.ThrowIfCancellationRequested(); - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary) - { - var nodes = doc.SelectNodes("//fanart/music/albums/*"); - if (nodes != null) - { - artist.AlbumCovers = new Dictionary(); - foreach (XmlNode node in nodes) - { - - var key = node.Attributes["id"] != null ? node.Attributes["id"].Value : null; - var cover = node.SelectSingleNode("albumcover/@url"); - path = cover != null ? cover.Value : null; - - if (!string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(key)) - { - Logger.Debug("FanArtProvider getting Album Cover for " + artist.Name); - artist.AlbumCovers[key] = path; - } - } - - } - - } - - cancellationToken.ThrowIfCancellationRequested(); - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Art && !item.ResolveArgs.ContainsMetaFileByName(ART_FILE)) { var node = @@ -209,10 +183,10 @@ namespace MediaBrowser.Controller.Providers.Music path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting ClearArt for " + artist.Name); + Logger.Debug("FanArtProvider getting ClearArt for " + item.Name); try { - artist.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(artist, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ART_FILE, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -232,10 +206,10 @@ namespace MediaBrowser.Controller.Providers.Music path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting Banner for " + artist.Name); + Logger.Debug("FanArtProvider getting Banner for " + item.Name); try { - artist.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(artist, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BANNER_FILE, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -256,10 +230,10 @@ namespace MediaBrowser.Controller.Providers.Music path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting Primary image for " + artist.Name); + Logger.Debug("FanArtProvider getting Primary image for " + item.Name); try { - artist.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(artist, path, PRIMARY_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, path, PRIMARY_FILE, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -272,7 +246,7 @@ namespace MediaBrowser.Controller.Providers.Music } } } - SetLastRefreshed(artist, DateTime.UtcNow); + SetLastRefreshed(item, DateTime.UtcNow); return true; } } diff --git a/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs index 697a6604cb..b489991762 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs @@ -1,14 +1,15 @@ -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; +using MoreLinq; +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers.Music { @@ -33,26 +34,7 @@ namespace MediaBrowser.Controller.Providers.Music protected override async Task FetchLastfmData(BaseItem item, string id, CancellationToken cancellationToken) { - // Get albu info using artist and album name - var url = RootUrl + string.Format("method=album.getInfo&artist={0}&album={1}&api_key={2}&format=json", UrlEncode(item.Parent.Name), UrlEncode(item.Name), ApiKey); - - LastfmGetAlbumResult result; - - try - { - using (var json = await HttpClient.Get(url, LastfmResourcePool, cancellationToken).ConfigureAwait(false)) - { - result = JsonSerializer.DeserializeFromStream(json); - } - } - catch (HttpException e) - { - if (e.StatusCode == HttpStatusCode.NotFound) - { - throw new LastfmProviderException(string.Format("Unable to retrieve album info for {0} with artist {1}", item.Name, item.Parent.Name)); - } - throw; - } + var result = await GetAlbumResult(item, cancellationToken).ConfigureAwait(false); if (result != null && result.album != null) { @@ -71,9 +53,60 @@ namespace MediaBrowser.Controller.Providers.Music } } + private async Task GetAlbumResult(BaseItem item, CancellationToken cancellationToken) + { + var result = await GetAlbumResult(item.Parent.Name, item.Name, cancellationToken); + + if (result != null && result.album != null) + { + return result; + } + + var folder = (Folder)item; + + // Get each song, distinct by the combination of AlbumArtist and Album + var songs = folder.Children.OfType protected string LocalMetaFileName { get; set; } + protected virtual bool SaveLocalMeta + { + get + { + return ConfigurationManager.Configuration.SaveLocalMeta; + } + } + /// /// If we save locally, refresh if they delete something /// @@ -91,7 +90,7 @@ namespace MediaBrowser.Controller.Providers.Music { get { - return ConfigurationManager.Configuration.SaveLocalMeta; + return SaveLocalMeta; } } @@ -173,16 +172,15 @@ namespace MediaBrowser.Controller.Providers.Music { if (item.DontFetchMeta) return false; - if (ConfigurationManager.Configuration.SaveLocalMeta && HasFileSystemStampChanged(item, providerInfo)) + if (RefreshOnFileSystemStampChange && HasFileSystemStampChanged(item, providerInfo)) { //If they deleted something from file system, chances are, this item was mis-identified the first time item.SetProviderId(MetadataProviders.Musicbrainz, null); Logger.Debug("LastfmProvider reports file system stamp change..."); return true; - } - if (providerInfo.LastRefreshStatus == ProviderRefreshStatus.CompletedWithErrors) + if (providerInfo.LastRefreshStatus != ProviderRefreshStatus.Success) { Logger.Debug("LastfmProvider for {0} - last attempt had errors. Will try again.", item.Path); return true; @@ -194,22 +192,10 @@ namespace MediaBrowser.Controller.Providers.Music return true; } - var downloadDate = providerInfo.LastRefreshed; + if (DateTime.UtcNow.Subtract(providerInfo.LastRefreshed).TotalDays > ConfigurationManager.Configuration.MetadataRefreshDays) // only refresh every n days + return true; - if (ConfigurationManager.Configuration.MetadataRefreshDays == -1 && downloadDate != DateTime.MinValue) - { - return false; - } - - if (DateTime.Today.Subtract(item.DateCreated).TotalDays > 180 && downloadDate != DateTime.MinValue) - return false; // don't trigger a refresh data for item that are more than 6 months old and have been refreshed before - - if (DateTime.Today.Subtract(downloadDate).TotalDays < ConfigurationManager.Configuration.MetadataRefreshDays) // only refresh every n days - return false; - - - Logger.Debug("LastfmProvider - " + item.Name + " needs refresh. Download date: " + downloadDate + " item created date: " + item.DateCreated + " Check for Update age: " + ConfigurationManager.Configuration.MetadataRefreshDays); - return true; + return false; } /// @@ -221,36 +207,9 @@ namespace MediaBrowser.Controller.Providers.Music /// Task{System.Boolean}. public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { - if (item.DontFetchMeta) - { - Logger.Info("LastfmProvider - Not fetching because requested to ignore " + item.Name); - return false; - } - cancellationToken.ThrowIfCancellationRequested(); - BaseProviderInfo providerData; - - if (!item.ProviderData.TryGetValue(Id, out providerData)) - { - providerData = new BaseProviderInfo(); - } - - if (!ConfigurationManager.Configuration.SaveLocalMeta || !HasLocalMeta(item) || (force && !HasLocalMeta(item)) || (RefreshOnVersionChange && providerData.ProviderVersion != ProviderVersion)) - { - try - { - await FetchData(item, cancellationToken).ConfigureAwait(false); - SetLastRefreshed(item, DateTime.UtcNow); - } - catch (LastfmProviderException) - { - SetLastRefreshed(item, DateTime.UtcNow, ProviderRefreshStatus.CompletedWithErrors); - } - - return true; - } - Logger.Debug("LastfmProvider not fetching because local meta exists for " + item.Name); + await FetchData(item, cancellationToken).ConfigureAwait(false); SetLastRefreshed(item, DateTime.UtcNow); return true; } diff --git a/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs b/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs index 842d10e4d5..442dd4b694 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs @@ -6,20 +6,23 @@ namespace MediaBrowser.Controller.Providers.Music { public static class LastfmHelper { - public static string LocalArtistMetaFileName = "MBArtist.json"; - public static string LocalAlbumMetaFileName = "MBAlbum.json"; + public static string LocalArtistMetaFileName = "mbartist.js"; + public static string LocalAlbumMetaFileName = "mbalbum.js"; public static void ProcessArtistData(BaseItem artist, LastfmArtist data) { - var overview = data.bio != null ? data.bio.content : null; - - artist.Overview = overview; - var yearFormed = 0; if (data.bio != null) { Int32.TryParse(data.bio.yearformed, out yearFormed); + + artist.Overview = data.bio.content; + + if (!string.IsNullOrEmpty(data.bio.placeformed)) + { + artist.AddProductionLocation(data.bio.placeformed); + } } artist.PremiereDate = yearFormed > 0 ? new DateTime(yearFormed, 1,1) : DateTime.MinValue; @@ -52,7 +55,10 @@ namespace MediaBrowser.Controller.Providers.Music { foreach (var tag in tags.tag) { - item.AddGenre(tag.name); + if (!string.IsNullOrEmpty(tag.name)) + { + item.AddGenre(tag.name); + } } } } diff --git a/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs index a7fc4586f1..cdbfb0883c 100644 --- a/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs @@ -100,7 +100,7 @@ namespace MediaBrowser.Controller.Providers.TV Logger.Debug("FanArtProvider getting ClearLogo for " + series.Name); try { - series.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(series, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + series.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(series, path, LOGO_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -124,7 +124,7 @@ namespace MediaBrowser.Controller.Providers.TV Logger.Debug("FanArtProvider getting ClearArt for " + series.Name); try { - series.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(series, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + series.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(series, path, ART_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -148,7 +148,7 @@ namespace MediaBrowser.Controller.Providers.TV Logger.Debug("FanArtProvider getting ThumbArt for " + series.Name); try { - series.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(series, path, THUMB_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + series.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(series, path, THUMB_FILE, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { diff --git a/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs index b5c6df7ecf..74cb1bfd4b 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs @@ -233,7 +233,7 @@ namespace MediaBrowser.Controller.Providers.TV try { - episode.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(episode, TVUtils.BannerUrl + p, Path.GetFileName(p), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken); + episode.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(episode, TVUtils.BannerUrl + p, Path.GetFileName(p), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken); } catch (HttpException) { diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs index f39e91834f..9d1a7c2566 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs @@ -177,7 +177,7 @@ namespace MediaBrowser.Controller.Providers.TV try { if (n != null) - season.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false); + season.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false); } catch (HttpException) { @@ -204,7 +204,7 @@ namespace MediaBrowser.Controller.Providers.TV TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), - RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken). + ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken). ConfigureAwait(false); season.SetImage(ImageType.Banner, bannerImagePath); @@ -231,7 +231,7 @@ namespace MediaBrowser.Controller.Providers.TV try { if (season.BackdropImagePaths == null) season.BackdropImagePaths = new List(); - season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false)); + season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -265,7 +265,7 @@ namespace MediaBrowser.Controller.Providers.TV "backdrop" + Path.GetExtension( n.InnerText), - RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken) + ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken) .ConfigureAwait(false)); } catch (HttpException) diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs index 89cb89289e..5a68981bf5 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs @@ -228,7 +228,7 @@ namespace MediaBrowser.Controller.Providers.TV string n = doc.SafeGetString("//banner"); if (!string.IsNullOrWhiteSpace(n)) { - series.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n, "banner" + Path.GetExtension(n), TvDbResourcePool, cancellationToken).ConfigureAwait(false)); + series.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n, "banner" + Path.GetExtension(n), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false)); } string s = doc.SafeGetString("//Network"); @@ -369,7 +369,7 @@ namespace MediaBrowser.Controller.Providers.TV { try { - series.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), TvDbResourcePool, cancellationToken).ConfigureAwait(false); + series.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false); } catch (HttpException) { @@ -392,7 +392,7 @@ namespace MediaBrowser.Controller.Providers.TV { try { - var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), TvDbResourcePool, cancellationToken); + var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken); series.SetImage(ImageType.Banner, bannerImagePath); } @@ -421,7 +421,7 @@ namespace MediaBrowser.Controller.Providers.TV { try { - series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), TvDbResourcePool, cancellationToken).ConfigureAwait(false)); + series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, TvDbResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config new file mode 100644 index 0000000000..44996f0e82 --- /dev/null +++ b/MediaBrowser.Controller/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 4d66d14227..692176efca 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Progress; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -103,7 +104,7 @@ namespace MediaBrowser.Server.Implementations.Library private readonly IUserManager _userManager; private readonly IUserDataRepository _userDataRepository; - + /// /// Gets or sets the configuration manager. /// @@ -244,7 +245,7 @@ namespace MediaBrowser.Server.Implementations.Library { // Any number of configuration settings could change the way the library is refreshed, so do that now _taskManager.CancelIfRunningAndQueue(); - + if (refreshPeopleAfterUpdate) { _taskManager.CancelIfRunningAndQueue(); @@ -285,7 +286,7 @@ namespace MediaBrowser.Server.Implementations.Library items.AddRange(userFolders); - return new ConcurrentDictionary(items.ToDictionary(i => i.Id)); + return new ConcurrentDictionary(items.ToDictionary(i => i.Id)); } /// @@ -505,7 +506,7 @@ namespace MediaBrowser.Server.Implementations.Library { return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ?? (UserRootFolder)ResolvePath(userRootPath)); } - + /// /// Gets a Person /// @@ -560,7 +561,20 @@ namespace MediaBrowser.Server.Implementations.Library /// Task{Genre}. public Task GetArtist(string name, bool allowSlowProviders = false) { - return GetImagesByNameItem(ConfigurationManager.ApplicationPaths.ArtistsPath, name, CancellationToken.None, allowSlowProviders); + return GetArtist(name, CancellationToken.None, allowSlowProviders); + } + + /// + /// Gets the artist. + /// + /// The name. + /// The cancellation token. + /// if set to true [allow slow providers]. + /// if set to true [force creation]. + /// Task{Artist}. + private Task GetArtist(string name, CancellationToken cancellationToken, bool allowSlowProviders = false, bool forceCreation = false) + { + return GetImagesByNameItem(ConfigurationManager.ApplicationPaths.ArtistsPath, name, cancellationToken, allowSlowProviders, forceCreation); } /// @@ -764,6 +778,76 @@ namespace MediaBrowser.Server.Implementations.Library _logger.Info("People validation complete"); } + public async Task ValidateArtists(CancellationToken cancellationToken, IProgress progress) + { + const int maxTasks = 25; + + var tasks = new List(); + + var artists = RootFolder.RecursiveChildren + .OfType