using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers.Movies { /// /// Class MovieDbImagesProvider /// public class MovieDbImagesProvider : BaseMetadataProvider { /// /// The get images /// private const string GetImages = @"http://api.themoviedb.org/3/{2}/{0}/images?api_key={1}"; /// /// The _provider manager /// private readonly IProviderManager _providerManager; /// /// The _json serializer /// private readonly IJsonSerializer _jsonSerializer; /// /// The _HTTP client /// private readonly IHttpClient _httpClient; /// /// Initializes a new instance of the class. /// /// The log manager. /// The configuration manager. /// The provider manager. /// The json serializer. /// The HTTP client. public MovieDbImagesProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IJsonSerializer jsonSerializer, IHttpClient httpClient) : base(logManager, configurationManager) { _providerManager = providerManager; _jsonSerializer = jsonSerializer; _httpClient = httpClient; } /// /// Gets the priority. /// /// The priority. public override MetadataProviderPriority Priority { get { return MetadataProviderPriority.Fourth; } } /// /// Supports the specified item. /// /// The item. /// true if XXXX, false otherwise public override bool Supports(BaseItem item) { var trailer = item as Trailer; if (trailer != null) { return !trailer.IsLocalTrailer; } // Don't support local trailers return item is Movie || item is BoxSet; } /// /// Gets a value indicating whether [requires internet]. /// /// true if [requires internet]; otherwise, false. public override bool RequiresInternet { get { return true; } } /// /// If we save locally, refresh if they delete something /// /// true if [refresh on file system stamp change]; otherwise, false. protected override bool RefreshOnFileSystemStampChange { get { return ConfigurationManager.Configuration.SaveLocalMeta; } } /// /// Gets a value indicating whether [refresh on version change]. /// /// true if [refresh on version change]; otherwise, false. protected override bool RefreshOnVersionChange { get { return true; } } /// /// Gets the provider version. /// /// The provider version. protected override string ProviderVersion { get { return "3"; } } /// /// Needses the refresh internal. /// /// The item. /// The provider info. /// true if XXXX, false otherwise protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb))) { return false; } // Refresh if tmdb id has changed if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Tmdb))) { return true; } // Don't refresh if we already have both poster and backdrop and we're not refreshing images if (!ConfigurationManager.Configuration.RefreshItemImages && item.HasImage(ImageType.Primary) && item.BackdropImagePaths.Count > 0) { return false; } return base.NeedsRefreshInternal(item, providerInfo); } /// /// Fetches metadata and returns true or false indicating if any work that requires persistence was done /// /// The item. /// if set to true [force]. /// The cancellation token /// Task{System.Boolean}. public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { BaseProviderInfo data; if (!item.ProviderData.TryGetValue(Id, out data)) { data = new BaseProviderInfo(); item.ProviderData[Id] = data; } var images = await FetchImages(item, item.GetProviderId(MetadataProviders.Tmdb), cancellationToken).ConfigureAwait(false); var status = await ProcessImages(item, images, cancellationToken).ConfigureAwait(false); data.Data = GetComparisonData(item.GetProviderId(MetadataProviders.Tmdb)); SetLastRefreshed(item, DateTime.UtcNow, status); return true; } /// /// Gets the comparison data. /// /// Guid. private Guid GetComparisonData(string id) { return string.IsNullOrEmpty(id) ? Guid.Empty : id.GetMD5(); } /// /// Fetches the images. /// /// The item. /// The id. /// The cancellation token. /// Task{MovieImages}. private async Task FetchImages(BaseItem item, string id, CancellationToken cancellationToken) { using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = string.Format(GetImages, id, MovieDbProvider.ApiKey, item is BoxSet ? "collection" : "movie"), CancellationToken = cancellationToken, AcceptHeader = MovieDbProvider.AcceptHeader }).ConfigureAwait(false)) { return _jsonSerializer.DeserializeFromStream(json); } } /// /// Processes the images. /// /// The item. /// The images. /// The cancellation token /// Task. protected virtual async Task ProcessImages(BaseItem item, MovieImages images, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var status = ProviderRefreshStatus.Success; var hasLocalPoster = item.HasImage(ImageType.Primary); // poster if (images.posters != null && images.posters.Count > 0 && (ConfigurationManager.Configuration.RefreshItemImages || !hasLocalPoster)) { var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedPosterSize; // get highest rated poster for our language var postersSortedByVote = images.posters.OrderByDescending(i => i.vote_average); var poster = postersSortedByVote.FirstOrDefault(p => p.iso_639_1 != null && p.iso_639_1.Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, StringComparison.OrdinalIgnoreCase)); if (poster == null && !ConfigurationManager.Configuration.PreferredMetadataLanguage.Equals("en")) { // couldn't find our specific language, find english (if that wasn't our language) poster = postersSortedByVote.FirstOrDefault(p => p.iso_639_1 != null && p.iso_639_1.Equals("en", StringComparison.OrdinalIgnoreCase)); } if (poster == null) { //still couldn't find it - try highest rated null one poster = postersSortedByVote.FirstOrDefault(p => p.iso_639_1 == null); } if (poster == null) { //finally - just get the highest rated one poster = postersSortedByVote.FirstOrDefault(); } if (poster != null) { var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = tmdbImageUrl + poster.file_path, CancellationToken = cancellationToken }).ConfigureAwait(false); item.PrimaryImagePath = await _providerManager.SaveImage(item, img, "folder" + Path.GetExtension(poster.file_path), ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem, cancellationToken).ConfigureAwait(false); } } cancellationToken.ThrowIfCancellationRequested(); // backdrops - only download if earlier providers didn't find any (fanart) if (images.backdrops != null && images.backdrops.Count > 0 && ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && item.BackdropImagePaths.Count == 0) { item.BackdropImagePaths = new List(); var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); var tmdbImageUrl = tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedBackdropSize; //backdrops should be in order of rating. get first n ones var numToFetch = Math.Min(ConfigurationManager.Configuration.MaxBackdrops, images.backdrops.Count); for (var i = 0; i < numToFetch; i++) { var bdName = "backdrop" + (i == 0 ? "" : i.ToString(CultureInfo.InvariantCulture)); var hasLocalBackdrop = item.BackdropImagePaths.Count > i; if (ConfigurationManager.Configuration.RefreshItemImages || !hasLocalBackdrop) { var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions { Url = tmdbImageUrl + images.backdrops[i].file_path, CancellationToken = cancellationToken }).ConfigureAwait(false); item.BackdropImagePaths.Add(await _providerManager.SaveImage(item, img, bdName + Path.GetExtension(images.backdrops[i].file_path), ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem, cancellationToken).ConfigureAwait(false)); } } } return status; } /// /// Class Backdrop /// protected class Backdrop { /// /// Gets or sets the file_path. /// /// The file_path. public string file_path { get; set; } /// /// Gets or sets the width. /// /// The width. public int width { get; set; } /// /// Gets or sets the height. /// /// The height. public int height { get; set; } /// /// Gets or sets the iso_639_1. /// /// The iso_639_1. public string iso_639_1 { get; set; } /// /// Gets or sets the aspect_ratio. /// /// The aspect_ratio. public double aspect_ratio { get; set; } /// /// Gets or sets the vote_average. /// /// The vote_average. public double vote_average { get; set; } /// /// Gets or sets the vote_count. /// /// The vote_count. public int vote_count { get; set; } } /// /// Class Poster /// protected class Poster { /// /// Gets or sets the file_path. /// /// The file_path. public string file_path { get; set; } /// /// Gets or sets the width. /// /// The width. public int width { get; set; } /// /// Gets or sets the height. /// /// The height. public int height { get; set; } /// /// Gets or sets the iso_639_1. /// /// The iso_639_1. public string iso_639_1 { get; set; } /// /// Gets or sets the aspect_ratio. /// /// The aspect_ratio. public double aspect_ratio { get; set; } /// /// Gets or sets the vote_average. /// /// The vote_average. public double vote_average { get; set; } /// /// Gets or sets the vote_count. /// /// The vote_count. public int vote_count { get; set; } } /// /// Class MovieImages /// protected class MovieImages { /// /// Gets or sets the backdrops. /// /// The backdrops. public List backdrops { get; set; } /// /// Gets or sets the posters. /// /// The posters. public List posters { get; set; } } } }