From 192e36275b3caeb16a579d443a1a9e6e7bb515ec Mon Sep 17 00:00:00 2001 From: softworkz Date: Thu, 23 Jun 2016 03:27:25 +0200 Subject: [PATCH] OMDB Episode Provider: Use new full season API for retrieval and caching --- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 200 ++++++++++++++++++ .../TV/Omdb/OmdbEpisodeProvider.cs | 22 +- 2 files changed, 204 insertions(+), 18 deletions(-) diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 4056073f2..397107c74 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Serialization; using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -114,6 +115,106 @@ namespace MediaBrowser.Providers.Omdb ParseAdditionalMetadata(item, result); } + public async Task FetchEpisodeData(BaseItem item, int episodeNumber, int seasonNumber, string imdbId, string language, string country, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(imdbId)) + { + throw new ArgumentNullException("imdbId"); + } + + var seasonResult = await GetSeasonRootObject(imdbId, seasonNumber, cancellationToken); + + RootObject result = null; + + foreach (var episode in seasonResult.Episodes) + { + if (episode.Episode == episodeNumber) + { + result = episode; + break; + } + } + + if (result == null) + { + return false; + } + + + // Only take the name and rating if the user's language is set to english, since Omdb has no localization + if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) + { + item.Name = result.Title; + + if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase)) + { + item.OfficialRating = result.Rated; + } + } + + int year; + + if (!string.IsNullOrEmpty(result.Year) + && int.TryParse(result.Year, NumberStyles.Number, _usCulture, out year) + && year >= 0) + { + item.ProductionYear = year; + } + + var hasCriticRating = item as IHasCriticRating; + if (hasCriticRating != null) + { + // Seeing some bogus RT data on omdb for series, so filter it out here + // RT doesn't even have tv series + int tomatoMeter; + + if (!string.IsNullOrEmpty(result.tomatoMeter) + && int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter) + && tomatoMeter >= 0) + { + hasCriticRating.CriticRating = tomatoMeter; + } + + if (!string.IsNullOrEmpty(result.tomatoConsensus) + && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) + { + hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus); + } + } + + int voteCount; + + if (!string.IsNullOrEmpty(result.imdbVotes) + && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount) + && voteCount >= 0) + { + item.VoteCount = voteCount; + } + + float imdbRating; + + if (!string.IsNullOrEmpty(result.imdbRating) + && float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating) + && imdbRating >= 0) + { + item.CommunityRating = imdbRating; + } + + if (!string.IsNullOrEmpty(result.Website)) + { + item.HomePageUrl = result.Website; + } + + if (!string.IsNullOrWhiteSpace(result.imdbID)) + { + item.SetProviderId(MetadataProviders.Imdb, result.imdbID); + } + + ParseAdditionalMetadata(item, result); + + return true; + } + internal async Task GetRootObject(string imdbId, CancellationToken cancellationToken) { var path = await EnsureItemInfo(imdbId, cancellationToken); @@ -133,6 +234,40 @@ namespace MediaBrowser.Providers.Omdb return result; } + internal async Task GetSeasonRootObject(string imdbId, int seasonId, CancellationToken cancellationToken) + { + var path = await EnsureSeasonInfo(imdbId, seasonId, cancellationToken); + + string resultString; + + using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072)) + { + using (var reader = new StreamReader(stream, new UTF8Encoding(false))) + { + resultString = reader.ReadToEnd(); + resultString = resultString.Replace("\"N/A\"", "\"\""); + } + } + + var result = _jsonSerializer.DeserializeFromString(resultString); + return result; + } + + internal static bool IsValidSeries(Dictionary seriesProviderIds) + { + string id; + if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id) && !string.IsNullOrEmpty(id)) + { + // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. + if (!string.IsNullOrWhiteSpace(id)) + { + return true; + } + } + + return false; + } + private async Task EnsureItemInfo(string imdbId, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(imdbId)) @@ -173,6 +308,46 @@ namespace MediaBrowser.Providers.Omdb return path; } + private async Task EnsureSeasonInfo(string seriesImdbId, int seasonId, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(seriesImdbId)) + { + throw new ArgumentNullException("imdbId"); + } + + var imdbParam = seriesImdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? seriesImdbId : "tt" + seriesImdbId; + + var path = GetSeasonFilePath(imdbParam, seasonId); + + var fileInfo = _fileSystem.GetFileSystemInfo(path); + + if (fileInfo.Exists) + { + // If it's recent or automatic updates are enabled, don't re-download + if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3) + { + return path; + } + } + + var url = string.Format("https://www.omdbapi.com/?i={0}&season={1}&detail=full", imdbParam, seasonId); + + using (var stream = await _httpClient.Get(new HttpRequestOptions + { + Url = url, + ResourcePool = ResourcePool, + CancellationToken = cancellationToken + + }).ConfigureAwait(false)) + { + var rootObject = _jsonSerializer.DeserializeFromStream(stream); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); + } + + return path; + } + internal string GetDataFilePath(string imdbId) { if (string.IsNullOrEmpty(imdbId)) @@ -187,6 +362,20 @@ namespace MediaBrowser.Providers.Omdb return Path.Combine(dataPath, filename); } + internal string GetSeasonFilePath(string imdbId, int seasonId) + { + if (string.IsNullOrEmpty(imdbId)) + { + throw new ArgumentNullException("imdbId"); + } + + var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb"); + + var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId); + + return Path.Combine(dataPath, filename); + } + private void ParseAdditionalMetadata(BaseItem item, RootObject result) { // Grab series genres because imdb data is better than tvdb. Leave movies alone @@ -238,12 +427,23 @@ namespace MediaBrowser.Providers.Omdb return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase); } + internal class SeasonRootObject + { + public string Title { get; set; } + public string seriesID { get; set; } + public int Season { get; set; } + public int? totalSeasons { get; set; } + public RootObject[] Episodes { get; set; } + public string Response { get; set; } + } + internal class RootObject { public string Title { get; set; } public string Year { get; set; } public string Rated { get; set; } public string Released { get; set; } + public int Episode { get; set; } public string Runtime { get; set; } public string Genre { get; set; } public string Director { get; set; } diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs index ebbefbeb1..21668f014 100644 --- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { - var result = new MetadataResult + var result = new MetadataResult() { Item = new Episode() }; @@ -53,30 +53,16 @@ namespace MediaBrowser.Providers.TV return result; } - var imdbId = info.GetProviderId(MetadataProviders.Imdb); - if (string.IsNullOrWhiteSpace(imdbId)) + if (OmdbProvider.IsValidSeries(info.SeriesProviderIds) && info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) { - imdbId = await GetEpisodeImdbId(info, cancellationToken).ConfigureAwait(false); - } + var seriesImdbId = info.SeriesProviderIds[MetadataProviders.Imdb.ToString()]; - if (!string.IsNullOrEmpty(imdbId)) - { - result.Item.SetProviderId(MetadataProviders.Imdb, imdbId); - result.HasMetadata = true; - - await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).FetchEpisodeData(result.Item, info.IndexNumber.Value, info.ParentIndexNumber.Value, seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; } - private async Task GetEpisodeImdbId(EpisodeInfo info, CancellationToken cancellationToken) - { - var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false); - var first = results.FirstOrDefault(); - return first == null ? null : first.GetProviderId(MetadataProviders.Imdb); - } - public int Order { get