|  | @@ -108,6 +108,29 @@ namespace MediaBrowser.Controller.Providers.Movies
 | 
	
		
			
				|  |  |              return item is Movie;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Gets the comparison data.
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <param name="imdbId">The imdb id.</param>
 | 
	
		
			
				|  |  | +        /// <returns>Guid.</returns>
 | 
	
		
			
				|  |  | +        private Guid GetComparisonData(string imdbId)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return string.IsNullOrEmpty(imdbId) ? Guid.Empty : imdbId.GetMD5();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Gets the priority.
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <value>The priority.</value>
 | 
	
		
			
				|  |  | +        public override MetadataProviderPriority Priority
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            get
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                // Run after moviedb and xml providers
 | 
	
		
			
				|  |  | +                return MetadataProviderPriority.Last;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Needses the refresh internal.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
	
		
			
				|  | @@ -116,6 +139,8 @@ namespace MediaBrowser.Controller.Providers.Movies
 | 
	
		
			
				|  |  |          /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
 | 
	
		
			
				|  |  |          protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              // Refresh if imdb id has changed
 | 
	
		
			
				|  |  |              if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Imdb)))
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -134,114 +159,116 @@ namespace MediaBrowser.Controller.Providers.Movies
 | 
	
		
			
				|  |  |          /// <returns>Task{System.Boolean}.</returns>
 | 
	
		
			
				|  |  |          public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            // Lastly, record the Imdb id here
 | 
	
		
			
				|  |  |              BaseProviderInfo data;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            // See whether there's an IMDB Id available
 | 
	
		
			
				|  |  |              string imdbId = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |              if (item.ProviderData.TryGetValue(Id, out data))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  imdbId = item.GetProviderId(MetadataProviders.Imdb);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            else
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                // Wat
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              RTMovieSearchResult hit = null;
 | 
	
		
			
				|  |  | -            Stream stream = null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if (string.IsNullOrEmpty(imdbId))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  // No IMDB Id, search RT for an ID
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  int page = 1;
 | 
	
		
			
				|  |  | -                stream = HttpClient.Get(MovieSearchUrl(item.Name, page), _rottenTomatoesResourcePool, cancellationToken).Result;
 | 
	
		
			
				|  |  | -                RTSearchResults result = JsonSerializer.DeserializeFromStream<RTSearchResults>(stream);
 | 
	
		
			
				|  |  | -  
 | 
	
		
			
				|  |  | -                if (result.total == 1)
 | 
	
		
			
				|  |  | +                using (Stream stream = HttpClient.Get(MovieSearchUrl(item.Name, page), _rottenTomatoesResourcePool, cancellationToken).Result)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    hit = result.movies[0];
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                else if (result.total > 1)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    while (hit == null)
 | 
	
		
			
				|  |  | +                    RTSearchResults result = JsonSerializer.DeserializeFromStream<RTSearchResults>(stream);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    if (result.total == 1)
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  | -                        // See if there's an absolute hit somewhere
 | 
	
		
			
				|  |  | -                        foreach (var searchHit in result.movies)
 | 
	
		
			
				|  |  | -                        {   
 | 
	
		
			
				|  |  | -                            if (string.Equals(searchHit.title, item.Name, StringComparison.InvariantCultureIgnoreCase))
 | 
	
		
			
				|  |  | -                            {
 | 
	
		
			
				|  |  | -                                hit = searchHit;
 | 
	
		
			
				|  |  | -                                break;
 | 
	
		
			
				|  |  | -                            }
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | +                        // With only one result we'll have to assume that this is the movie we're searching for
 | 
	
		
			
				|  |  | +                        hit = result.movies[0];
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else if (result.total > 1)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        // If there are more results than one
 | 
	
		
			
				|  |  | +                        // Step 1: Loop through all current results, see if there's an exact match (not case sensitive) somewhere, if so, accept that as the searched item, else go to step 2
 | 
	
		
			
				|  |  | +                        // Step 2: Retrieve the next page and go to step 1 if there are results, else, stop checking
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        if (hit == null)
 | 
	
		
			
				|  |  | +                        while (hit == null)
 | 
	
		
			
				|  |  |                          {
 | 
	
		
			
				|  |  | -                            Stream newPageStream = HttpClient.Get(MovieSearchUrl(item.Name, page++), _rottenTomatoesResourcePool, cancellationToken).Result;
 | 
	
		
			
				|  |  | -                            result = JsonSerializer.DeserializeFromStream<RTSearchResults>(stream);
 | 
	
		
			
				|  |  | +                            foreach (var searchHit in result.movies)
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                if (string.Equals(searchHit.title, item.Name, StringComparison.InvariantCultureIgnoreCase))
 | 
	
		
			
				|  |  | +                                {
 | 
	
		
			
				|  |  | +                                    hit = searchHit;
 | 
	
		
			
				|  |  | +                                    break;
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                            if (result.total == 0)
 | 
	
		
			
				|  |  | +                            if (hit == null)
 | 
	
		
			
				|  |  |                              {
 | 
	
		
			
				|  |  | -                                // No results found on RottenTomatoes
 | 
	
		
			
				|  |  | -                                break;
 | 
	
		
			
				|  |  | +                                using (Stream newPageStream = HttpClient.Get(MovieSearchUrl(item.Name, page++), _rottenTomatoesResourcePool, cancellationToken).Result)
 | 
	
		
			
				|  |  | +                                {
 | 
	
		
			
				|  |  | +                                    result = JsonSerializer.DeserializeFromStream<RTSearchResults>(stream);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                                    if (result.total == 0)
 | 
	
		
			
				|  |  | +                                    {
 | 
	
		
			
				|  |  | +                                        // No results found on RottenTomatoes
 | 
	
		
			
				|  |  | +                                        break;
 | 
	
		
			
				|  |  | +                                    }
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  |                              }
 | 
	
		
			
				|  |  | +                            // while end
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | +                // if null end
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              else
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  // Have IMDB Id
 | 
	
		
			
				|  |  | -                stream = HttpClient.Get(MovieImdbUrl(imdbId), _rottenTomatoesResourcePool, cancellationToken).Result;
 | 
	
		
			
				|  |  | -                RTMovieSearchResult result = JsonSerializer.DeserializeFromStream<RTMovieSearchResult>(stream);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if (!string.IsNullOrEmpty(result.id))
 | 
	
		
			
				|  |  | +                using (Stream stream = HttpClient.Get(MovieImdbUrl(imdbId), _rottenTomatoesResourcePool, cancellationToken).Result)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    // got a result
 | 
	
		
			
				|  |  | -                    hit = result;
 | 
	
		
			
				|  |  | +                    RTMovieSearchResult result = JsonSerializer.DeserializeFromStream<RTMovieSearchResult>(stream);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    if (!string.IsNullOrEmpty(result.id))
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        // Got a result
 | 
	
		
			
				|  |  | +                        hit = result;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            stream.Dispose();
 | 
	
		
			
				|  |  | -            stream = null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            // If we found any results, that's great!
 | 
	
		
			
				|  |  |              if (hit != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  item.CriticRatingSummary = hit.critics_concensus;
 | 
	
		
			
				|  |  |                  item.CriticRating = float.Parse(hit.ratings.critics_score);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                stream = HttpClient.Get(MovieReviewsUrl(hit.id), _rottenTomatoesResourcePool, cancellationToken).Result;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                RTReviewList result = JsonSerializer.DeserializeFromStream<RTReviewList>(stream);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                item.CriticReviews.Clear();
 | 
	
		
			
				|  |  | -                foreach (var rtReview in result.reviews)
 | 
	
		
			
				|  |  | +                using (Stream stream = HttpClient.Get(MovieReviewsUrl(hit.id), _rottenTomatoesResourcePool, cancellationToken).Result)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    ItemReview review = new ItemReview();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    review.ReviewerName = rtReview.critic;
 | 
	
		
			
				|  |  | -                    review.Publisher = rtReview.publication;
 | 
	
		
			
				|  |  | -                    review.Date = DateTime.Parse(rtReview.date).ToUniversalTime();
 | 
	
		
			
				|  |  | -                    review.Caption = rtReview.quote;
 | 
	
		
			
				|  |  | -                    review.Url = rtReview.links.review;
 | 
	
		
			
				|  |  | -                    item.CriticReviews.Add(review);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                if (data == null)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    data = new BaseProviderInfo();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                    RTReviewList result = JsonSerializer.DeserializeFromStream<RTReviewList>(stream);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    item.CriticReviews.Clear();
 | 
	
		
			
				|  |  | +                    foreach (var rtReview in result.reviews)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        ItemReview review = new ItemReview();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        review.ReviewerName = rtReview.critic;
 | 
	
		
			
				|  |  | +                        review.Publisher = rtReview.publication;
 | 
	
		
			
				|  |  | +                        review.Date = DateTime.Parse(rtReview.date).ToUniversalTime();
 | 
	
		
			
				|  |  | +                        review.Caption = rtReview.quote;
 | 
	
		
			
				|  |  | +                        review.Url = rtReview.links.review;
 | 
	
		
			
				|  |  | +                        item.CriticReviews.Add(review);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                data.Data = GetComparisonData(hit.alternate_ids.imdb);
 | 
	
		
			
				|  |  | +                    if (data == null)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        data = new BaseProviderInfo();
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                item.SetProviderId(MetadataProviders.Imdb, hit.alternate_ids.imdb);
 | 
	
		
			
				|  |  | -                item.SetProviderId(MetadataProviders.RottenTomatoes, hit.id);
 | 
	
		
			
				|  |  | +                    data.Data = GetComparisonData(hit.alternate_ids.imdb);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                stream.Dispose();
 | 
	
		
			
				|  |  | -                stream = null;
 | 
	
		
			
				|  |  | +                    item.SetProviderId(MetadataProviders.Imdb, hit.alternate_ids.imdb);
 | 
	
		
			
				|  |  | +                    item.SetProviderId(MetadataProviders.RottenTomatoes, hit.id);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              else
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -254,13 +281,17 @@ namespace MediaBrowser.Controller.Providers.Movies
 | 
	
		
			
				|  |  |              if (data != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  data.Data = GetComparisonData(imdbId);
 | 
	
		
			
				|  |  | +                data.LastRefreshStatus = ProviderRefreshStatus.Success;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              SetLastRefreshed(item, DateTime.UtcNow);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              return Task.FromResult(true);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        // Utility functions to get the URL of the API calls
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private string MovieUrl(string rtId)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              return BasicUrl + string.Format(Movie, ApiKey, rtId);
 | 
	
	
		
			
				|  | @@ -281,28 +312,6 @@ namespace MediaBrowser.Controller.Providers.Movies
 | 
	
		
			
				|  |  |              return BasicUrl + string.Format(MoviesReviews, ApiKey, rtId);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the comparison data.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="imdbId">The imdb id.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Guid.</returns>
 | 
	
		
			
				|  |  | -        private Guid GetComparisonData(string imdbId)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            return string.IsNullOrEmpty(imdbId) ? Guid.Empty : imdbId.GetMD5();
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the priority.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <value>The priority.</value>
 | 
	
		
			
				|  |  | -        public override MetadataProviderPriority Priority
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            get
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                // Run after moviedb and xml providers
 | 
	
		
			
				|  |  | -                return MetadataProviderPriority.Last;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // Data contract classes for use with the Rotten Tomatoes API
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -361,7 +370,7 @@ namespace MediaBrowser.Controller.Providers.Movies
 | 
	
		
			
				|  |  |              public RTReviewLink links { get; set; }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        protected class RTReviewLink 
 | 
	
		
			
				|  |  | +        protected class RTReviewLink
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              public string review { get; set; }
 | 
	
		
			
				|  |  |          }
 |