From 524150331c1811e8ced5ff8444e0d0197bc6419a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 30 Oct 2013 17:33:27 -0400 Subject: [PATCH] beginning manual image providers --- MediaBrowser.Api/LibraryService.cs | 30 +++- .../MediaBrowser.Controller.csproj | 1 + .../Providers/IImageProvider.cs | 38 +++++ .../Providers/IProviderManager.cs | 13 +- .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + .../Providers/RemoteImageInfo.cs | 51 +++++++ .../MediaBrowser.Providers.csproj | 1 + .../Movies/ManualMovieDbImageProvider.cs | 133 ++++++++++++++++++ .../Movies/MovieDbImagesProvider.cs | 62 +++----- .../Providers/ProviderManager.cs | 64 ++++++++- .../ApplicationHost.cs | 4 +- 13 files changed, 355 insertions(+), 49 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/IImageProvider.cs create mode 100644 MediaBrowser.Model/Providers/RemoteImageInfo.cs create mode 100644 MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 202d372e83..5b133fbd3e 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -5,8 +5,10 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; using ServiceStack.ServiceHost; using System; @@ -32,6 +34,21 @@ namespace MediaBrowser.Api public string Id { get; set; } } + [Route("/Items/{Id}/RemoteImages/{Type}", "GET")] + [Api(Description = "Gets available remote images for an item")] + public class GetRemoteImages : IReturn> + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + + [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public ImageType Type { get; set; } + } + /// /// Class GetCriticReviews /// @@ -208,6 +225,7 @@ namespace MediaBrowser.Api private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; + private readonly IProviderManager _providerManager; private readonly IDtoService _dtoService; @@ -215,13 +233,14 @@ namespace MediaBrowser.Api /// Initializes a new instance of the class. /// public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, - IDtoService dtoService, IUserDataManager userDataManager) + IDtoService dtoService, IUserDataManager userDataManager, IProviderManager providerManager) { _itemRepo = itemRepo; _libraryManager = libraryManager; _userManager = userManager; _dtoService = dtoService; _userDataManager = userDataManager; + _providerManager = providerManager; } public object Get(GetFile request) @@ -240,6 +259,15 @@ namespace MediaBrowser.Api return ToStaticFileResult(item.Path); } + public object Get(GetRemoteImages request) + { + var item = _dtoService.GetItemByDtoId(request.Id); + + var result = _providerManager.GetAvailableRemoteImages(item, request.Type, CancellationToken.None).Result; + + return ToOptimizedResult(result); + } + /// /// Gets the specified request. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 77db7d2c24..c379714aa3 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -109,6 +109,7 @@ + diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs new file mode 100644 index 0000000000..0764794388 --- /dev/null +++ b/MediaBrowser.Controller/Providers/IImageProvider.cs @@ -0,0 +1,38 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Providers +{ + /// + /// Interface IImageProvider + /// + public interface IImageProvider + { + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Supportses the specified item. + /// + /// The item. + /// Type of the image. + /// true if XXXX, false otherwise + bool Supports(BaseItem item, ImageType imageType); + + /// + /// Gets the available images. + /// + /// The item. + /// Type of the image. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 6a4d132b76..2eb2be6db1 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using System.Collections.Generic; using System.IO; using System.Threading; @@ -52,6 +53,16 @@ namespace MediaBrowser.Controller.Providers /// Adds the metadata providers. /// /// The providers. - void AddParts(IEnumerable providers); + /// The image providers. + void AddParts(IEnumerable providers, IEnumerable imageProviders); + + /// + /// Gets the available remote images. + /// + /// The item. + /// The type. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + Task> GetAvailableRemoteImages(BaseItem item, ImageType type, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index e88953b9c1..0f54073ef5 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -287,6 +287,9 @@ Plugins\PluginInfo.cs + + Providers\RemoteImageInfo.cs + Querying\ArtistsQuery.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 6a14817a60..3d4b8d2c7d 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -274,6 +274,9 @@ Plugins\PluginInfo.cs + + Providers\RemoteImageInfo.cs + Querying\ArtistsQuery.cs diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index caf89346cd..b048dc1c3e 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -59,6 +59,7 @@ + diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs new file mode 100644 index 0000000000..bb2a3cb69c --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -0,0 +1,51 @@ + +namespace MediaBrowser.Model.Providers +{ + /// + /// Class RemoteImageInfo + /// + public class RemoteImageInfo + { + /// + /// Gets or sets the name of the provider. + /// + /// The name of the provider. + public string ProviderName { get; set; } + + /// + /// Gets or sets the URL. + /// + /// The URL. + public string Url { get; set; } + + /// + /// Gets or sets the height. + /// + /// The height. + public int? Height { get; set; } + + /// + /// Gets or sets the width. + /// + /// The width. + public int? Width { get; set; } + + /// + /// Gets or sets the community rating. + /// + /// The community rating. + public double? CommunityRating { get; set; } + + /// + /// Gets or sets the vote count. + /// + /// The vote count. + public int? VoteCount { get; set; } + + /// + /// Gets or sets the language. + /// + /// The language. + public string Language { get; set; } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 63bd8c9534..7a8975937d 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -60,6 +60,7 @@ + diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs new file mode 100644 index 0000000000..39b7fa6c67 --- /dev/null +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -0,0 +1,133 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Movies +{ + class ManualMovieDbImageProvider : IImageProvider + { + private readonly IJsonSerializer _jsonSerializer; + private readonly IServerConfigurationManager _config; + + public ManualMovieDbImageProvider(IJsonSerializer jsonSerializer, IServerConfigurationManager config) + { + _jsonSerializer = jsonSerializer; + _config = config; + } + + public string Name + { + get { return "TheMovieDB"; } + } + + public bool Supports(BaseItem item, ImageType imageType) + { + if (MovieDbImagesProvider.SupportsItem(item)) + { + return imageType == ImageType.Primary || imageType == ImageType.Backdrop; + } + + return false; + } + + public async Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) + { + var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var results = MovieDbImagesProvider.FetchImages(item, _jsonSerializer); + + var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + + if (imageType == ImageType.Primary) + { + var sources = GetPosters(results, item); + + return sources.Select(i => new RemoteImageInfo + { + Url = tmdbImageUrl + i.file_path, + CommunityRating = i.vote_average, + VoteCount = i.vote_count, + Width = i.width, + Height = i.height, + Language = i.iso_639_1, + ProviderName = Name + }); + } + + if (imageType == ImageType.Backdrop) + { + var sources = GetBackdrops(results, item); + + return sources.Select(i => new RemoteImageInfo + { + Url = tmdbImageUrl + i.file_path, + CommunityRating = i.vote_average, + VoteCount = i.vote_count, + Width = i.width, + Height = i.height, + ProviderName = Name + }); + } + + throw new ArgumentException("Unrecognized ImageType: " + imageType); + } + + /// + /// Gets the posters. + /// + /// The images. + /// The item. + /// IEnumerable{MovieDbProvider.Poster}. + public IEnumerable GetPosters(MovieDbProvider.Images images, BaseItem item) + { + var language = _config.Configuration.PreferredMetadataLanguage; + + var eligiblePosters = images.posters == null ? + new List() : + images.posters.Where(i => i.width >= _config.Configuration.MinMoviePosterWidth) + .ToList(); + + return eligiblePosters.OrderByDescending(i => i.vote_average) + .ThenByDescending(i => + { + if (string.Equals(language, i.iso_639_1, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (string.Equals("en", i.iso_639_1, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + if (string.IsNullOrEmpty(i.iso_639_1)) + { + return 1; + } + return 0; + }) + .ToList(); + } + + /// + /// Gets the backdrops. + /// + /// The images. + /// The item. + /// IEnumerable{MovieDbProvider.Backdrop}. + public IEnumerable GetBackdrops(MovieDbProvider.Images images, BaseItem item) + { + var eligibleBackdrops = images.backdrops == null ? new List() : + images.backdrops.Where(i => i.width >= _config.Configuration.MinMovieBackdropWidth) + .ToList(); + + return eligibleBackdrops.OrderByDescending(i => i.vote_average); + } + } +} diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs index 52e6c214f9..0fd61a96be 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs @@ -65,6 +65,11 @@ namespace MediaBrowser.Providers.Movies /// The item. /// true if XXXX, false otherwise public override bool Supports(BaseItem item) + { + return SupportsItem(item); + } + + public static bool SupportsItem(BaseItem item) { var trailer = item as Trailer; @@ -180,7 +185,7 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(id)) { - var images = FetchImages(item); + var images = FetchImages(item, _jsonSerializer); if (images != null) { @@ -196,8 +201,9 @@ namespace MediaBrowser.Providers.Movies /// Fetches the images. /// /// The item. + /// The json serializer. /// Task{MovieImages}. - private MovieDbProvider.Images FetchImages(BaseItem item) + internal static MovieDbProvider.Images FetchImages(BaseItem item, IJsonSerializer jsonSerializer) { var path = MovieDbProvider.Current.GetDataFilePath(item, "default"); @@ -207,7 +213,7 @@ namespace MediaBrowser.Providers.Movies if (fileInfo.Exists) { - return _jsonSerializer.DeserializeFromFile(path).images; + return jsonSerializer.DeserializeFromFile(path).images; } } @@ -227,13 +233,9 @@ namespace MediaBrowser.Providers.Movies var status = ProviderRefreshStatus.Success; - var eligiblePosters = images.posters == null ? - new List() : - images.posters.Where(i => i.width >= ConfigurationManager.Configuration.MinMoviePosterWidth) + var eligiblePosters = new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetPosters(images, item) .ToList(); - eligiblePosters = eligiblePosters.OrderByDescending(i => i.vote_average).ToList(); - // poster if (eligiblePosters.Count > 0 && !item.HasImage(ImageType.Primary)) { @@ -242,48 +244,24 @@ namespace MediaBrowser.Providers.Movies var tmdbImageUrl = tmdbSettings.images.base_url + "original"; // get highest rated poster for our language - var poster = eligiblePosters.FirstOrDefault(p => string.Equals(p.iso_639_1, ConfigurationManager.Configuration.PreferredMetadataLanguage, StringComparison.OrdinalIgnoreCase)); + var poster = eligiblePosters[0]; - if (poster == null) + var url = tmdbImageUrl + poster.file_path; + + var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { - // couldn't find our specific language, find english - poster = eligiblePosters.FirstOrDefault(p => string.Equals(p.iso_639_1, "en", StringComparison.OrdinalIgnoreCase)); - } + Url = url, + CancellationToken = cancellationToken - if (poster == null) - { - //still couldn't find it - try highest rated null one - poster = eligiblePosters.FirstOrDefault(p => p.iso_639_1 == null); - } + }).ConfigureAwait(false); - if (poster == null) - { - //finally - just get the highest rated one - poster = eligiblePosters.FirstOrDefault(); - } - - if (poster != null) - { - var url = tmdbImageUrl + poster.file_path; - - var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken - - }).ConfigureAwait(false); - - await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(poster.file_path), ImageType.Primary, null, url, cancellationToken) - .ConfigureAwait(false); - - } + await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(poster.file_path), ImageType.Primary, null, url, cancellationToken) + .ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); - var eligibleBackdrops = images.backdrops == null ? new List() : - images.backdrops.Where(i => i.width >= ConfigurationManager.Configuration.MinMovieBackdropWidth) - .ToList(); + var eligibleBackdrops = new ManualMovieDbImageProvider(_jsonSerializer, ConfigurationManager).GetBackdrops(images, item).ToList(); var backdropLimit = ConfigurationManager.Configuration.MaxBackdrops; diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index 19230cecdb..1b086a0f91 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -13,6 +13,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Server.Implementations.Providers { @@ -48,6 +49,8 @@ namespace MediaBrowser.Server.Implementations.Providers /// The metadata providers enumerable. private BaseMetadataProvider[] MetadataProviders { get; set; } + private IImageProvider[] ImageProviders { get; set; } + /// /// Initializes a new instance of the class. /// @@ -55,8 +58,7 @@ namespace MediaBrowser.Server.Implementations.Providers /// The configuration manager. /// The directory watchers. /// The log manager. - /// The library manager. - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, ILibraryManager libraryManager) + public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager) { _logger = logManager.GetLogger("ProviderManager"); _httpClient = httpClient; @@ -68,9 +70,12 @@ namespace MediaBrowser.Server.Implementations.Providers /// Adds the metadata providers. /// /// The providers. - public void AddParts(IEnumerable providers) + /// The image providers. + public void AddParts(IEnumerable providers, IEnumerable imageProviders) { MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); + + ImageProviders = imageProviders.ToArray(); } /// @@ -344,5 +349,58 @@ namespace MediaBrowser.Server.Implementations.Providers { return new ImageSaver(ConfigurationManager, _directoryWatchers).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken); } + + /// + /// Gets the available remote images. + /// + /// The item. + /// The type. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + public async Task> GetAvailableRemoteImages(BaseItem item, ImageType type, CancellationToken cancellationToken) + { + var providers = GetSupportedImageProviders(item, type); + + var tasks = providers.Select(i => Task.Run(async () => + { + try + { + var result = await i.GetAvailableImages(item, type, cancellationToken).ConfigureAwait(false); + return result.ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("{0} failed in GetAvailableImages for type {1}", ex, i.GetType().Name, item.GetType().Name); + return new List(); + } + })); + + var results = await Task.WhenAll(tasks).ConfigureAwait(false); + + return results.SelectMany(i => i); + } + + /// + /// Gets the supported image providers. + /// + /// The item. + /// The type. + /// IEnumerable{IImageProvider}. + private IEnumerable GetSupportedImageProviders(BaseItem item, ImageType type) + { + return ImageProviders.Where(i => + { + try + { + return i.Supports(item, type); + } + catch (Exception ex) + { + _logger.ErrorException("{0} failed in Supports for type {1}", ex, i.GetType().Name, item.GetType().Name); + return false; + } + + }); + } } } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index c277ee5098..4aac1bb919 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -275,7 +275,7 @@ namespace MediaBrowser.ServerApplication DirectoryWatchers = new DirectoryWatchers(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager); RegisterSingleInstance(DirectoryWatchers); - ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager, LibraryManager); + ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, DirectoryWatchers, LogManager); RegisterSingleInstance(ProviderManager); RegisterSingleInstance(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager)); @@ -449,7 +449,7 @@ namespace MediaBrowser.ServerApplication GetExports(), GetExports()); - ProviderManager.AddParts(GetExports()); + ProviderManager.AddParts(GetExports(), GetExports()); ImageProcessor.AddParts(GetExports());