OMDB Episode Provider: Use new full season API for retrieval and caching

This commit is contained in:
softworkz 2016-06-23 03:27:25 +02:00
parent 461e9b2cdd
commit 192e36275b
2 changed files with 204 additions and 18 deletions

View File

@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -114,6 +115,106 @@ namespace MediaBrowser.Providers.Omdb
ParseAdditionalMetadata(item, result); ParseAdditionalMetadata(item, result);
} }
public async Task<bool> 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<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken) internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
{ {
var path = await EnsureItemInfo(imdbId, cancellationToken); var path = await EnsureItemInfo(imdbId, cancellationToken);
@ -133,6 +234,40 @@ namespace MediaBrowser.Providers.Omdb
return result; return result;
} }
internal async Task<SeasonRootObject> 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<SeasonRootObject>(resultString);
return result;
}
internal static bool IsValidSeries(Dictionary<string, string> 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<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken) private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
{ {
if (string.IsNullOrWhiteSpace(imdbId)) if (string.IsNullOrWhiteSpace(imdbId))
@ -173,6 +308,46 @@ namespace MediaBrowser.Providers.Omdb
return path; return path;
} }
private async Task<string> 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<SeasonRootObject>(stream);
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
}
return path;
}
internal string GetDataFilePath(string imdbId) internal string GetDataFilePath(string imdbId)
{ {
if (string.IsNullOrEmpty(imdbId)) if (string.IsNullOrEmpty(imdbId))
@ -187,6 +362,20 @@ namespace MediaBrowser.Providers.Omdb
return Path.Combine(dataPath, filename); 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) private void ParseAdditionalMetadata(BaseItem item, RootObject result)
{ {
// Grab series genres because imdb data is better than tvdb. Leave movies alone // 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); 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 internal class RootObject
{ {
public string Title { get; set; } public string Title { get; set; }
public string Year { get; set; } public string Year { get; set; }
public string Rated { get; set; } public string Rated { get; set; }
public string Released { get; set; } public string Released { get; set; }
public int Episode { get; set; }
public string Runtime { get; set; } public string Runtime { get; set; }
public string Genre { get; set; } public string Genre { get; set; }
public string Director { get; set; } public string Director { get; set; }

View File

@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.TV
public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken)
{ {
var result = new MetadataResult<Episode> var result = new MetadataResult<Episode>()
{ {
Item = new Episode() Item = new Episode()
}; };
@ -53,30 +53,16 @@ namespace MediaBrowser.Providers.TV
return result; return result;
} }
var imdbId = info.GetProviderId(MetadataProviders.Imdb); if (OmdbProvider.IsValidSeries(info.SeriesProviderIds) && info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
if (string.IsNullOrWhiteSpace(imdbId))
{ {
imdbId = await GetEpisodeImdbId(info, cancellationToken).ConfigureAwait(false); var seriesImdbId = info.SeriesProviderIds[MetadataProviders.Imdb.ToString()];
}
if (!string.IsNullOrEmpty(imdbId)) 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);
{
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);
} }
return result; return result;
} }
private async Task<string> 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 public int Order
{ {
get get