@ -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 < 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 )
{
var path = await EnsureItemInfo ( imdbId , cancellationToken ) ;
@ -133,6 +234,40 @@ namespace MediaBrowser.Providers.Omdb
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 )
{
if ( string . IsNullOrWhiteSpace ( imdbId ) )
@ -173,6 +308,46 @@ namespace MediaBrowser.Providers.Omdb
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 )
{
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 ; }