2013-10-14 17:34:54 +00:00
using MediaBrowser.Common.Configuration ;
2013-10-31 14:03:23 +00:00
using MediaBrowser.Common.IO ;
2013-10-14 17:34:54 +00:00
using MediaBrowser.Common.Net ;
2013-03-04 05:43:06 +00:00
using MediaBrowser.Controller.Configuration ;
2013-02-21 01:33:05 +00:00
using MediaBrowser.Controller.Entities ;
using MediaBrowser.Controller.Entities.Movies ;
2013-06-09 16:47:28 +00:00
using MediaBrowser.Controller.Providers ;
2013-02-21 01:33:05 +00:00
using MediaBrowser.Model.Entities ;
using MediaBrowser.Model.Logging ;
2013-03-04 05:43:06 +00:00
using MediaBrowser.Model.Serialization ;
2013-10-04 15:22:03 +00:00
using MediaBrowser.Providers.Savers ;
2013-02-21 01:33:05 +00:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
2013-03-04 05:43:06 +00:00
using System.Net ;
2013-02-21 01:33:05 +00:00
using System.Threading ;
using System.Threading.Tasks ;
2013-06-09 16:47:28 +00:00
namespace MediaBrowser.Providers.Movies
2013-02-21 01:33:05 +00:00
{
/// <summary>
/// Class MovieDbProvider
/// </summary>
2013-03-08 05:08:27 +00:00
public class MovieDbProvider : BaseMetadataProvider , IDisposable
2013-02-21 01:33:05 +00:00
{
2013-04-22 15:26:56 +00:00
protected static CultureInfo EnUs = new CultureInfo ( "en-US" ) ;
2013-03-08 05:08:27 +00:00
protected readonly IProviderManager ProviderManager ;
2013-04-13 18:02:30 +00:00
2013-03-04 05:43:06 +00:00
/// <summary>
/// The movie db
/// </summary>
2013-10-13 17:52:57 +00:00
internal readonly SemaphoreSlim MovieDbResourcePool = new SemaphoreSlim ( 1 , 1 ) ;
2013-03-04 05:43:06 +00:00
internal static MovieDbProvider Current { get ; private set ; }
2013-02-24 21:53:54 +00:00
/// <summary>
/// Gets the json serializer.
/// </summary>
/// <value>The json serializer.</value>
protected IJsonSerializer JsonSerializer { get ; private set ; }
2013-02-25 00:13:45 +00:00
/// <summary>
/// Gets the HTTP client.
/// </summary>
/// <value>The HTTP client.</value>
protected IHttpClient HttpClient { get ; private set ; }
2013-10-31 14:03:23 +00:00
private readonly IFileSystem _fileSystem ;
2013-02-25 00:13:45 +00:00
2013-02-24 21:53:54 +00:00
/// <summary>
/// Initializes a new instance of the <see cref="MovieDbProvider" /> class.
/// </summary>
2013-03-04 05:43:06 +00:00
/// <param name="logManager">The log manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
2013-02-24 21:53:54 +00:00
/// <param name="jsonSerializer">The json serializer.</param>
2013-02-25 00:13:45 +00:00
/// <param name="httpClient">The HTTP client.</param>
2013-04-10 15:56:36 +00:00
/// <param name="providerManager">The provider manager.</param>
2013-10-31 14:03:23 +00:00
public MovieDbProvider ( ILogManager logManager , IServerConfigurationManager configurationManager , IJsonSerializer jsonSerializer , IHttpClient httpClient , IProviderManager providerManager , IFileSystem fileSystem )
2013-03-04 05:43:06 +00:00
: base ( logManager , configurationManager )
2013-02-24 21:53:54 +00:00
{
JsonSerializer = jsonSerializer ;
2013-02-25 00:13:45 +00:00
HttpClient = httpClient ;
2013-03-08 05:08:27 +00:00
ProviderManager = providerManager ;
2013-10-31 14:03:23 +00:00
_fileSystem = fileSystem ;
2013-03-04 05:43:06 +00:00
Current = this ;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
2013-03-08 05:08:27 +00:00
protected virtual void Dispose ( bool dispose )
2013-03-04 05:43:06 +00:00
{
if ( dispose )
{
2013-10-13 17:52:57 +00:00
MovieDbResourcePool . Dispose ( ) ;
2013-03-04 05:43:06 +00:00
}
2013-02-24 21:53:54 +00:00
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
public override MetadataProviderPriority Priority
{
2013-06-13 18:45:58 +00:00
get { return MetadataProviderPriority . Third ; }
2013-02-21 01:33:05 +00:00
}
/// <summary>
/// Supportses the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public override bool Supports ( BaseItem item )
{
2013-05-06 15:37:02 +00:00
var trailer = item as Trailer ;
if ( trailer ! = null )
{
return ! trailer . IsLocalTrailer ;
}
// Don't support local trailers
2013-05-28 01:59:26 +00:00
return item is Movie | | item is BoxSet | | item is MusicVideo ;
2013-02-21 01:33:05 +00:00
}
/// <summary>
/// Gets a value indicating whether [requires internet].
/// </summary>
/// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value>
public override bool RequiresInternet
{
get
{
return true ;
}
}
2013-05-06 22:52:13 +00:00
protected override bool RefreshOnVersionChange
{
get
{
return true ;
}
}
protected override string ProviderVersion
{
get
{
2013-10-17 15:34:47 +00:00
return "3" ;
2013-05-06 22:52:13 +00:00
}
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// The _TMDB settings task
/// </summary>
2013-05-19 19:37:52 +00:00
private TmdbSettingsResult _tmdbSettings ;
private readonly SemaphoreSlim _tmdbSettingsSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
2013-02-21 01:33:05 +00:00
/// <summary>
/// Gets the TMDB settings.
/// </summary>
2013-05-19 19:37:52 +00:00
/// <returns>Task{TmdbSettingsResult}.</returns>
internal async Task < TmdbSettingsResult > GetTmdbSettings ( CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
if ( _tmdbSettings ! = null )
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
return _tmdbSettings ;
2013-02-21 01:33:05 +00:00
}
2013-05-19 19:37:52 +00:00
await _tmdbSettingsSemaphore . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
try
{
2013-09-05 21:34:46 +00:00
// Check again in case it got populated while we were waiting.
if ( _tmdbSettings ! = null )
{
return _tmdbSettings ;
}
2013-05-19 18:42:58 +00:00
using ( var json = await GetMovieDbResponse ( new HttpRequestOptions
2013-05-04 04:15:39 +00:00
{
Url = string . Format ( TmdbConfigUrl , ApiKey ) ,
2013-05-19 19:37:52 +00:00
CancellationToken = cancellationToken ,
2013-05-21 04:04:38 +00:00
AcceptHeader = AcceptHeader
2013-05-04 04:15:39 +00:00
} ) . ConfigureAwait ( false ) )
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
_tmdbSettings = JsonSerializer . DeserializeFromStream < TmdbSettingsResult > ( json ) ;
return _tmdbSettings ;
2013-02-21 01:33:05 +00:00
}
}
2013-05-19 19:37:52 +00:00
finally
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
_tmdbSettingsSemaphore . Release ( ) ;
2013-02-21 01:33:05 +00:00
}
}
private const string TmdbConfigUrl = "http://api.themoviedb.org/3/configuration?api_key={0}" ;
2013-08-26 14:36:41 +00:00
private const string Search3 = @"http://api.themoviedb.org/3/search/{3}?api_key={1}&query={0}&language={2}" ;
2013-10-22 19:52:33 +00:00
private const string GetMovieInfo3 = @"http://api.themoviedb.org/3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers" ;
private const string GetBoxSetInfo3 = @"http://api.themoviedb.org/3/collection/{0}?api_key={1}&append_to_response=images" ;
2013-05-06 22:52:13 +00:00
2013-05-04 04:15:39 +00:00
internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669" ;
internal static string AcceptHeader = "application/json,image/*" ;
2013-02-21 01:33:05 +00:00
protected override bool NeedsRefreshInternal ( BaseItem item , BaseProviderInfo providerInfo )
{
2013-11-01 19:54:25 +00:00
if ( string . IsNullOrEmpty ( item . GetProviderId ( MetadataProviders . Tmdb ) ) )
2013-06-28 20:25:58 +00:00
{
return true ;
}
2013-05-19 19:37:52 +00:00
return base . NeedsRefreshInternal ( item , providerInfo ) ;
2013-02-21 01:33:05 +00:00
}
2013-10-14 17:34:54 +00:00
protected override bool NeedsRefreshBasedOnCompareDate ( BaseItem item , BaseProviderInfo providerInfo )
{
2013-11-01 15:55:25 +00:00
var path = GetDataFilePath ( item ) ;
2013-10-14 17:34:54 +00:00
if ( ! string . IsNullOrEmpty ( path ) )
{
var fileInfo = new FileInfo ( path ) ;
2013-12-27 03:48:05 +00:00
return ! fileInfo . Exists | | _fileSystem . GetLastWriteTimeUtc ( fileInfo ) > providerInfo . LastRefreshed ;
2013-10-14 17:34:54 +00:00
}
2013-11-02 19:30:29 +00:00
return base . NeedsRefreshBasedOnCompareDate ( item , providerInfo ) ;
2013-10-14 17:34:54 +00:00
}
/// <summary>
/// Gets the movie data path.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="isBoxSet">if set to <c>true</c> [is box set].</param>
/// <param name="tmdbId">The TMDB id.</param>
/// <returns>System.String.</returns>
internal static string GetMovieDataPath ( IApplicationPaths appPaths , bool isBoxSet , string tmdbId )
{
var dataPath = isBoxSet ? GetBoxSetsDataPath ( appPaths ) : GetMoviesDataPath ( appPaths ) ;
return Path . Combine ( dataPath , tmdbId ) ;
}
internal static string GetMoviesDataPath ( IApplicationPaths appPaths )
{
var dataPath = Path . Combine ( appPaths . DataPath , "tmdb-movies" ) ;
return dataPath ;
}
internal static string GetBoxSetsDataPath ( IApplicationPaths appPaths )
{
var dataPath = Path . Combine ( appPaths . DataPath , "tmdb-collections" ) ;
return dataPath ;
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
/// <param name="item">The item.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
2013-03-03 16:05:17 +00:00
/// <param name="cancellationToken">The cancellation token</param>
2013-02-21 01:33:05 +00:00
/// <returns>Task{System.Boolean}.</returns>
2013-12-06 20:07:34 +00:00
public override async Task < bool > FetchAsync ( BaseItem item , bool force , BaseProviderInfo providerInfo , CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
2013-10-14 17:34:54 +00:00
var id = item . GetProviderId ( MetadataProviders . Tmdb ) ;
if ( string . IsNullOrEmpty ( id ) )
{
id = item . GetProviderId ( MetadataProviders . Imdb ) ;
}
2013-12-15 01:17:57 +00:00
// Don't search for music video id's because it is very easy to misidentify.
if ( string . IsNullOrEmpty ( id ) & & ! ( item is MusicVideo ) )
2013-10-14 17:34:54 +00:00
{
id = await FindId ( item , cancellationToken ) . ConfigureAwait ( false ) ;
}
if ( ! string . IsNullOrEmpty ( id ) )
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
await FetchMovieData ( item , id , force , cancellationToken ) . ConfigureAwait ( false ) ;
}
2013-05-19 19:37:52 +00:00
2013-12-06 20:07:34 +00:00
SetLastRefreshed ( item , DateTime . UtcNow , providerInfo ) ;
2013-02-21 01:33:05 +00:00
return true ;
}
/// <summary>
/// Determines whether [has alt meta] [the specified item].
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if [has alt meta] [the specified item]; otherwise, <c>false</c>.</returns>
2013-06-25 01:22:21 +00:00
internal static bool HasAltMeta ( BaseItem item )
2013-02-21 01:33:05 +00:00
{
2013-06-27 14:51:40 +00:00
if ( item is BoxSet )
{
return item . LocationType = = LocationType . FileSystem & & item . ResolveArgs . ContainsMetaFileByName ( "collection.xml" ) ;
}
2013-08-26 15:14:59 +00:00
2013-10-01 18:24:27 +00:00
var path = MovieXmlSaver . GetMovieSavePath ( item ) ;
2013-08-26 15:14:59 +00:00
2013-10-01 18:24:27 +00:00
if ( item . LocationType = = LocationType . FileSystem )
{
// If mixed with multiple movies in one folder, resolve args won't have the file system children
return item . ResolveArgs . ContainsMetaFileByName ( Path . GetFileName ( path ) ) | | File . Exists ( path ) ;
}
return false ;
2013-02-21 01:33:05 +00:00
}
/// <summary>
/// Finds the id.
/// </summary>
/// <param name="item">The item.</param>
2013-03-03 16:05:17 +00:00
/// <param name="cancellationToken">The cancellation token</param>
2013-02-21 01:33:05 +00:00
/// <returns>Task{System.String}.</returns>
2013-08-24 04:30:45 +00:00
public async Task < string > FindId ( BaseItem item , CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
2013-08-24 04:30:45 +00:00
int? yearInName ;
2013-02-21 01:33:05 +00:00
string name = item . Name ;
2013-12-02 02:24:14 +00:00
NameParser . ParseName ( name , out name , out yearInName ) ;
2013-02-21 01:33:05 +00:00
2013-08-24 04:30:45 +00:00
var year = item . ProductionYear ? ? yearInName ;
2013-02-21 01:33:05 +00:00
2013-08-26 14:36:41 +00:00
Logger . Info ( "MovieDbProvider: Finding id for item: " + name ) ;
2013-12-27 00:23:58 +00:00
var language = item . GetPreferredMetadataLanguage ( ) . ToLower ( ) ;
2013-02-21 01:33:05 +00:00
//if we are a boxset - look at our first child
var boxset = item as BoxSet ;
if ( boxset ! = null )
{
2013-09-23 14:02:56 +00:00
// See if any movies have a collection id already
2013-08-26 14:36:41 +00:00
var collId = boxset . Children . Concat ( boxset . GetLinkedChildren ( ) ) . OfType < Video > ( )
2013-06-28 20:25:58 +00:00
. Select ( i = > i . GetProviderId ( MetadataProviders . TmdbCollection ) )
. FirstOrDefault ( i = > i ! = null ) ;
2013-08-26 14:36:41 +00:00
if ( collId ! = null ) return collId ;
2013-02-21 01:33:05 +00:00
}
2013-06-28 20:25:58 +00:00
2013-02-21 01:33:05 +00:00
//nope - search for it
2013-08-26 14:36:41 +00:00
var searchType = item is BoxSet ? "collection" : "movie" ;
var id = await AttemptFindId ( name , searchType , year , language , cancellationToken ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
if ( id = = null )
{
//try in english if wasn't before
if ( language ! = "en" )
{
2013-08-26 14:36:41 +00:00
id = await AttemptFindId ( name , searchType , year , "en" , cancellationToken ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
}
else
{
// try with dot and _ turned to space
2013-05-06 18:10:44 +00:00
var originalName = name ;
2013-02-21 01:33:05 +00:00
name = name . Replace ( "," , " " ) ;
name = name . Replace ( "." , " " ) ;
name = name . Replace ( "_" , " " ) ;
2013-10-24 17:49:24 +00:00
name = name . Replace ( "-" , " " ) ;
2013-05-06 18:10:44 +00:00
// Search again if the new name is different
if ( ! string . Equals ( name , originalName ) )
2013-02-21 01:33:05 +00:00
{
2013-08-26 14:36:41 +00:00
id = await AttemptFindId ( name , searchType , year , language , cancellationToken ) . ConfigureAwait ( false ) ;
2013-05-06 18:10:44 +00:00
if ( id = = null & & language ! = "en" )
{
//one more time, in english
2013-08-26 14:36:41 +00:00
id = await AttemptFindId ( name , searchType , year , "en" , cancellationToken ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
2013-05-06 18:10:44 +00:00
}
2013-02-21 01:33:05 +00:00
}
2013-05-06 18:10:44 +00:00
2013-05-06 17:35:51 +00:00
if ( id = = null & & item . LocationType = = LocationType . FileSystem )
2013-02-21 01:33:05 +00:00
{
//last resort - try using the actual folder name
2013-05-06 18:10:44 +00:00
var pathName = Path . GetFileName ( item . ResolveArgs . Path ) ;
// Only search if it's a name we haven't already tried.
if ( ! string . Equals ( pathName , name , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( pathName , originalName , StringComparison . OrdinalIgnoreCase ) )
{
2013-08-26 14:36:41 +00:00
id = await AttemptFindId ( pathName , searchType , year , "en" , cancellationToken ) . ConfigureAwait ( false ) ;
2013-05-06 18:10:44 +00:00
}
2013-02-21 01:33:05 +00:00
}
}
}
2013-05-06 18:10:44 +00:00
2013-02-21 01:33:05 +00:00
return id ;
}
/// <summary>
/// Attempts the find id.
/// </summary>
/// <param name="name">The name.</param>
2013-08-26 14:36:41 +00:00
/// <param name="type">movie or collection</param>
2013-02-21 01:33:05 +00:00
/// <param name="year">The year.</param>
/// <param name="language">The language.</param>
2013-03-03 16:05:17 +00:00
/// <param name="cancellationToken">The cancellation token</param>
2013-02-21 01:33:05 +00:00
/// <returns>Task{System.String}.</returns>
2013-08-26 14:36:41 +00:00
private async Task < string > AttemptFindId ( string name , string type , int? year , string language , CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
2013-08-26 14:36:41 +00:00
string url3 = string . Format ( Search3 , UrlEncode ( name ) , ApiKey , language , type ) ;
2013-02-21 01:33:05 +00:00
TmdbMovieSearchResults searchResult = null ;
2013-05-19 19:37:52 +00:00
using ( Stream json = await GetMovieDbResponse ( new HttpRequestOptions
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
Url = url3 ,
CancellationToken = cancellationToken ,
2013-05-21 04:04:38 +00:00
AcceptHeader = AcceptHeader
2013-05-04 04:15:39 +00:00
2013-05-19 19:37:52 +00:00
} ) . ConfigureAwait ( false ) )
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
searchResult = JsonSerializer . DeserializeFromStream < TmdbMovieSearchResults > ( json ) ;
2013-02-21 01:33:05 +00:00
}
2013-05-19 19:37:52 +00:00
2013-02-21 01:33:05 +00:00
if ( searchResult ! = null )
{
2013-12-01 18:57:52 +00:00
return FindIdOfBestResult ( searchResult . results , name , year ) ;
}
return null ;
}
2013-02-21 01:33:05 +00:00
2013-12-01 18:57:52 +00:00
private string FindIdOfBestResult ( List < TmdbMovieSearchResult > results , string name , int? year )
{
if ( year . HasValue )
{
// Take the first result from the same year
var id = results . Where ( i = >
{
// Make sure it has a name
if ( ! string . IsNullOrEmpty ( i . title ? ? i . name ) )
2013-02-21 01:33:05 +00:00
{
2013-12-01 18:57:52 +00:00
DateTime r ;
// These dates are always in this exact format
if ( DateTime . TryParseExact ( i . release_date , "yyyy-MM-dd" , EnUs , DateTimeStyles . None , out r ) )
2013-02-21 01:33:05 +00:00
{
2013-12-01 18:57:52 +00:00
return r . Year = = year . Value ;
2013-02-21 01:33:05 +00:00
}
}
2013-12-01 18:57:52 +00:00
return false ;
} )
. Select ( i = > i . id . ToString ( CultureInfo . InvariantCulture ) )
. FirstOrDefault ( ) ;
if ( ! string . IsNullOrEmpty ( id ) )
{
return id ;
}
// Take the first result within one year
id = results . Where ( i = >
{
// Make sure it has a name
if ( ! string . IsNullOrEmpty ( i . title ? ? i . name ) )
{
DateTime r ;
// These dates are always in this exact format
if ( DateTime . TryParseExact ( i . release_date , "yyyy-MM-dd" , EnUs , DateTimeStyles . None , out r ) )
{
return Math . Abs ( r . Year - year . Value ) < = 1 ;
}
}
return false ;
} )
. Select ( i = > i . id . ToString ( CultureInfo . InvariantCulture ) )
. FirstOrDefault ( ) ;
if ( ! string . IsNullOrEmpty ( id ) )
{
return id ;
2013-02-21 01:33:05 +00:00
}
}
2013-12-01 18:57:52 +00:00
// Just take the first one
return results . Where ( i = > ! string . IsNullOrEmpty ( i . title ? ? i . name ) )
. Select ( i = > i . id . ToString ( CultureInfo . InvariantCulture ) )
. FirstOrDefault ( ) ;
2013-02-21 01:33:05 +00:00
}
/// <summary>
/// URLs the encode.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>System.String.</returns>
private static string UrlEncode ( string name )
{
return WebUtility . UrlEncode ( name ) ;
}
2013-10-14 17:34:54 +00:00
private readonly CultureInfo _usCulture = new CultureInfo ( "en-US" ) ;
2013-02-21 01:33:05 +00:00
/// <summary>
/// Fetches the movie data.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="id">The id.</param>
2013-10-14 17:34:54 +00:00
/// <param name="isForcedRefresh">if set to <c>true</c> [is forced refresh].</param>
2013-03-03 16:05:17 +00:00
/// <param name="cancellationToken">The cancellation token</param>
2013-02-21 01:33:05 +00:00
/// <returns>Task.</returns>
2013-10-14 17:34:54 +00:00
private async Task FetchMovieData ( BaseItem item , string id , bool isForcedRefresh , CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
2013-10-14 17:34:54 +00:00
// Id could be ImdbId or TmdbId
2013-12-27 00:23:58 +00:00
var language = item . GetPreferredMetadataLanguage ( ) ;
2013-12-28 16:58:13 +00:00
var country = item . GetPreferredMetadataCountryCode ( ) ;
2013-02-21 01:33:05 +00:00
2013-11-01 15:55:25 +00:00
var dataFilePath = GetDataFilePath ( item ) ;
2013-10-14 17:34:54 +00:00
2013-11-23 20:01:44 +00:00
var tmdbId = item . GetProviderId ( MetadataProviders . Tmdb ) ;
2013-12-27 03:48:05 +00:00
var isBoxSet = item is BoxSet ;
2013-10-14 17:34:54 +00:00
2013-12-27 03:48:05 +00:00
if ( string . IsNullOrEmpty ( dataFilePath ) | | ! File . Exists ( dataFilePath ) )
{
2013-10-22 19:03:21 +00:00
var mainResult = await FetchMainResult ( id , isBoxSet , language , cancellationToken ) . ConfigureAwait ( false ) ;
2013-10-14 17:34:54 +00:00
if ( mainResult = = null ) return ;
2013-11-23 20:01:44 +00:00
tmdbId = mainResult . id . ToString ( _usCulture ) ;
2013-12-28 16:58:13 +00:00
dataFilePath = GetDataFilePath ( isBoxSet , tmdbId , language ) ;
2013-10-14 17:34:54 +00:00
var directory = Path . GetDirectoryName ( dataFilePath ) ;
Directory . CreateDirectory ( directory ) ;
JsonSerializer . SerializeToFile ( mainResult , dataFilePath ) ;
2013-02-21 01:33:05 +00:00
}
2013-09-23 14:02:56 +00:00
2013-10-23 01:11:56 +00:00
if ( isForcedRefresh | | ConfigurationManager . Configuration . EnableTmdbUpdates | | ! HasAltMeta ( item ) )
2013-10-14 17:34:54 +00:00
{
2013-12-28 16:58:13 +00:00
dataFilePath = GetDataFilePath ( isBoxSet , tmdbId , language ) ;
2013-02-21 01:33:05 +00:00
2013-11-03 20:48:16 +00:00
if ( ! string . IsNullOrEmpty ( dataFilePath ) )
{
var mainResult = JsonSerializer . DeserializeFromFile < CompleteMovieData > ( dataFilePath ) ;
2013-10-14 17:34:54 +00:00
2013-11-03 20:48:16 +00:00
ProcessMainInfo ( item , mainResult ) ;
}
2013-10-14 17:34:54 +00:00
}
}
/// <summary>
/// Downloads the movie info.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="isBoxSet">if set to <c>true</c> [is box set].</param>
2013-12-27 00:23:58 +00:00
/// <param name="preferredMetadataLanguage">The preferred metadata language.</param>
2013-10-14 17:34:54 +00:00
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
2013-12-28 16:58:13 +00:00
internal async Task DownloadMovieInfo ( string id , bool isBoxSet , string preferredMetadataLanguage , CancellationToken cancellationToken )
2013-10-14 17:34:54 +00:00
{
2013-12-27 00:23:58 +00:00
var mainResult = await FetchMainResult ( id , isBoxSet , preferredMetadataLanguage , cancellationToken ) . ConfigureAwait ( false ) ;
2013-02-21 01:33:05 +00:00
if ( mainResult = = null ) return ;
2013-12-28 16:58:13 +00:00
var dataFilePath = GetDataFilePath ( isBoxSet , id , preferredMetadataLanguage ) ;
2013-10-14 17:34:54 +00:00
2013-12-28 16:58:13 +00:00
Directory . CreateDirectory ( Path . GetDirectoryName ( dataFilePath ) ) ;
2013-10-14 17:34:54 +00:00
JsonSerializer . SerializeToFile ( mainResult , dataFilePath ) ;
2013-02-21 01:33:05 +00:00
}
2014-01-10 13:52:01 +00:00
internal Task EnsureMovieInfo ( BaseItem item , CancellationToken cancellationToken )
{
var path = GetDataFilePath ( item ) ;
var fileInfo = _fileSystem . GetFileSystemInfo ( path ) ;
if ( fileInfo . Exists )
{
// If it's recent or automatic updates are enabled, don't re-download
if ( ConfigurationManager . Configuration . EnableTmdbUpdates | | ( DateTime . UtcNow - _fileSystem . GetLastWriteTimeUtc ( fileInfo ) ) . TotalDays < = 7 )
{
return Task . FromResult ( true ) ;
}
}
var id = item . GetProviderId ( MetadataProviders . Tmdb ) ;
if ( string . IsNullOrEmpty ( id ) )
{
return Task . FromResult ( true ) ;
}
return DownloadMovieInfo ( id , item is BoxSet , item . GetPreferredMetadataLanguage ( ) , cancellationToken ) ;
}
2013-02-21 01:33:05 +00:00
/// <summary>
2013-10-14 17:34:54 +00:00
/// Gets the data file path.
2013-02-21 01:33:05 +00:00
/// </summary>
/// <param name="item">The item.</param>
2013-10-14 17:34:54 +00:00
/// <returns>System.String.</returns>
2013-11-01 15:55:25 +00:00
internal string GetDataFilePath ( BaseItem item )
2013-10-14 17:34:54 +00:00
{
var id = item . GetProviderId ( MetadataProviders . Tmdb ) ;
if ( string . IsNullOrEmpty ( id ) )
{
return null ;
}
2013-12-28 16:58:13 +00:00
return GetDataFilePath ( item is BoxSet , id , item . GetPreferredMetadataLanguage ( ) ) ;
2013-11-23 20:01:44 +00:00
}
2014-01-10 13:52:01 +00:00
private string GetDataFilePath ( bool isBoxset , string tmdbId , string preferredLanguage )
2013-11-23 20:01:44 +00:00
{
2013-12-27 03:48:05 +00:00
var path = GetMovieDataPath ( ConfigurationManager . ApplicationPaths , isBoxset , tmdbId ) ;
2013-10-14 17:34:54 +00:00
2013-12-28 16:58:13 +00:00
var filename = string . Format ( "all-{0}.json" ,
preferredLanguage ? ? string . Empty ) ;
return Path . Combine ( path , filename ) ;
2013-11-01 15:55:25 +00:00
}
2013-10-14 17:34:54 +00:00
/// <summary>
/// Fetches the main result.
/// </summary>
2013-02-21 01:33:05 +00:00
/// <param name="id">The id.</param>
2013-10-14 17:34:54 +00:00
/// <param name="isBoxSet">if set to <c>true</c> [is box set].</param>
2013-10-22 19:03:21 +00:00
/// <param name="language">The language.</param>
2013-03-03 16:05:17 +00:00
/// <param name="cancellationToken">The cancellation token</param>
2013-02-21 01:33:05 +00:00
/// <returns>Task{CompleteMovieData}.</returns>
2013-10-22 19:52:33 +00:00
private async Task < CompleteMovieData > FetchMainResult ( string id , bool isBoxSet , string language , CancellationToken cancellationToken )
2013-02-21 01:33:05 +00:00
{
2013-10-14 17:34:54 +00:00
var baseUrl = isBoxSet ? GetBoxSetInfo3 : GetMovieInfo3 ;
2013-05-06 22:52:13 +00:00
2013-10-22 19:52:33 +00:00
var url = string . Format ( baseUrl , id , ApiKey ) ;
2013-12-27 03:48:05 +00:00
// Get images in english and with no language
url + = "&include_image_language=en,null" ;
2013-10-22 19:52:33 +00:00
if ( ! string . IsNullOrEmpty ( language ) )
{
2013-12-27 03:48:05 +00:00
// If preferred language isn't english, get those images too
if ( ! string . Equals ( language , "en" , StringComparison . OrdinalIgnoreCase ) )
{
url + = string . Format ( ",{0}" , language ) ;
}
url + = string . Format ( "&language={0}" , language ) ;
2013-10-22 19:52:33 +00:00
}
2013-03-03 16:05:17 +00:00
CompleteMovieData mainResult ;
2013-02-21 01:33:05 +00:00
cancellationToken . ThrowIfCancellationRequested ( ) ;
2013-05-19 19:37:52 +00:00
using ( var json = await GetMovieDbResponse ( new HttpRequestOptions
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
Url = url ,
CancellationToken = cancellationToken ,
2013-05-21 04:04:38 +00:00
AcceptHeader = AcceptHeader
2013-05-04 04:15:39 +00:00
2013-05-19 19:37:52 +00:00
} ) . ConfigureAwait ( false ) )
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
mainResult = JsonSerializer . DeserializeFromStream < CompleteMovieData > ( json ) ;
2013-02-21 01:33:05 +00:00
}
cancellationToken . ThrowIfCancellationRequested ( ) ;
if ( mainResult ! = null & & string . IsNullOrEmpty ( mainResult . overview ) )
{
2013-10-22 19:52:33 +00:00
if ( ! string . IsNullOrEmpty ( language ) & & ! string . Equals ( language , "en" , StringComparison . OrdinalIgnoreCase ) )
2013-02-21 01:33:05 +00:00
{
2013-10-22 19:03:21 +00:00
Logger . Info ( "MovieDbProvider couldn't find meta for language " + language + ". Trying English..." ) ;
2013-05-06 22:52:13 +00:00
url = string . Format ( baseUrl , id , ApiKey , "en" ) ;
2013-02-21 01:33:05 +00:00
2013-10-22 19:03:21 +00:00
using ( var json = await GetMovieDbResponse ( new HttpRequestOptions
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
Url = url ,
CancellationToken = cancellationToken ,
2013-05-21 04:04:38 +00:00
AcceptHeader = AcceptHeader
2013-05-04 04:15:39 +00:00
2013-05-19 19:37:52 +00:00
} ) . ConfigureAwait ( false ) )
2013-02-21 01:33:05 +00:00
{
2013-05-19 19:37:52 +00:00
mainResult = JsonSerializer . DeserializeFromStream < CompleteMovieData > ( json ) ;
2013-02-21 01:33:05 +00:00
}
if ( String . IsNullOrEmpty ( mainResult . overview ) )
{
2013-10-14 17:34:54 +00:00
Logger . Error ( "MovieDbProvider - Unable to find information for (id:" + id + ")" ) ;
2013-02-21 01:33:05 +00:00
return null ;
}
}
}
return mainResult ;
}
/// <summary>
/// Processes the main info.
/// </summary>
/// <param name="movie">The movie.</param>
/// <param name="movieData">The movie data.</param>
2013-10-22 19:52:33 +00:00
private void ProcessMainInfo ( BaseItem movie , CompleteMovieData movieData )
2013-02-21 01:33:05 +00:00
{
2013-11-23 20:01:44 +00:00
if ( ! movie . LockedFields . Contains ( MetadataFields . Name ) )
2013-02-21 01:33:05 +00:00
{
2013-11-23 20:01:44 +00:00
movie . Name = movieData . title ? ? movieData . original_title ? ? movieData . name ? ? movie . Name ;
}
if ( ! movie . LockedFields . Contains ( MetadataFields . Overview ) )
{
movie . Overview = WebUtility . HtmlDecode ( movieData . overview ) ;
movie . Overview = movie . Overview ! = null ? movie . Overview . Replace ( "\n\n" , "\n" ) : null ;
}
movie . HomePageUrl = movieData . homepage ;
2013-09-11 17:54:59 +00:00
2013-12-02 16:16:03 +00:00
var hasBudget = movie as IHasBudget ;
if ( hasBudget ! = null )
{
hasBudget . Budget = movieData . budget ;
hasBudget . Revenue = movieData . revenue ;
}
2013-04-12 14:13:47 +00:00
2013-11-23 20:01:44 +00:00
if ( ! string . IsNullOrEmpty ( movieData . tagline ) )
{
2013-12-05 16:50:21 +00:00
var hasTagline = movie as IHasTaglines ;
if ( hasTagline ! = null )
{
hasTagline . Taglines . Clear ( ) ;
hasTagline . AddTagline ( movieData . tagline ) ;
}
2013-11-23 20:01:44 +00:00
}
2013-04-28 05:44:45 +00:00
2013-11-23 20:01:44 +00:00
movie . SetProviderId ( MetadataProviders . Tmdb , movieData . id . ToString ( _usCulture ) ) ;
movie . SetProviderId ( MetadataProviders . Imdb , movieData . imdb_id ) ;
2013-05-07 03:20:51 +00:00
2013-11-23 20:01:44 +00:00
if ( movieData . belongs_to_collection ! = null )
{
movie . SetProviderId ( MetadataProviders . TmdbCollection ,
movieData . belongs_to_collection . id . ToString ( CultureInfo . InvariantCulture ) ) ;
2013-10-17 15:34:47 +00:00
2013-11-23 20:01:44 +00:00
var movieItem = movie as Movie ;
2013-10-17 15:34:47 +00:00
2013-11-23 20:01:44 +00:00
if ( movieItem ! = null )
2013-08-19 19:37:44 +00:00
{
2013-11-23 20:01:44 +00:00
movieItem . TmdbCollectionName = movieData . belongs_to_collection . name ;
2013-08-19 19:37:44 +00:00
}
2013-11-23 20:01:44 +00:00
}
else
{
movie . SetProviderId ( MetadataProviders . TmdbCollection , null ) ; // clear out any old entry
}
2013-05-07 03:20:51 +00:00
2013-11-23 20:01:44 +00:00
float rating ;
string voteAvg = movieData . vote_average . ToString ( CultureInfo . InvariantCulture ) ;
2013-07-05 17:40:51 +00:00
2013-11-23 20:01:44 +00:00
// tmdb appears to have unified their numbers to always report "7.3" regardless of country
// so I removed the culture-specific processing here because it was not working for other countries -ebr
// Movies get this from imdb
2013-12-26 14:20:17 +00:00
if ( ! ( movie is Movie ) & & float . TryParse ( voteAvg , NumberStyles . AllowDecimalPoint , CultureInfo . InvariantCulture , out rating ) )
2013-11-23 20:01:44 +00:00
{
movie . CommunityRating = rating ;
}
2013-02-21 01:33:05 +00:00
2013-11-23 20:01:44 +00:00
// Movies get this from imdb
2013-12-26 14:20:17 +00:00
if ( ! ( movie is Movie ) )
2013-11-23 20:01:44 +00:00
{
movie . VoteCount = movieData . vote_count ;
}
2013-12-28 16:58:13 +00:00
var preferredCountryCode = movie . GetPreferredMetadataCountryCode ( ) ;
2013-11-23 20:01:44 +00:00
//release date and certification are retrieved based on configured country and we fall back on US if not there and to minimun release date if still no match
if ( movieData . releases ! = null & & movieData . releases . countries ! = null )
{
2013-12-28 16:58:13 +00:00
var ourRelease = movieData . releases . countries . FirstOrDefault ( c = > c . iso_3166_1 . Equals ( preferredCountryCode , StringComparison . OrdinalIgnoreCase ) ) ? ? new Country ( ) ;
2013-11-23 20:01:44 +00:00
var usRelease = movieData . releases . countries . FirstOrDefault ( c = > c . iso_3166_1 . Equals ( "US" , StringComparison . OrdinalIgnoreCase ) ) ? ? new Country ( ) ;
var minimunRelease = movieData . releases . countries . OrderBy ( c = > c . release_date ) . FirstOrDefault ( ) ? ? new Country ( ) ;
if ( ! movie . LockedFields . Contains ( MetadataFields . OfficialRating ) )
2013-10-14 17:34:54 +00:00
{
2013-12-28 16:58:13 +00:00
var ratingPrefix = string . Equals ( preferredCountryCode , "us" , StringComparison . OrdinalIgnoreCase ) ? "" : preferredCountryCode + "-" ;
2013-11-23 20:01:44 +00:00
movie . OfficialRating = ! string . IsNullOrEmpty ( ourRelease . certification )
? ratingPrefix + ourRelease . certification
: ! string . IsNullOrEmpty ( usRelease . certification )
? usRelease . certification
: ! string . IsNullOrEmpty ( minimunRelease . certification )
? minimunRelease . iso_3166_1 + "-" + minimunRelease . certification
: null ;
2013-10-14 17:34:54 +00:00
}
2013-11-23 20:01:44 +00:00
}
2013-12-29 18:53:56 +00:00
if ( movieData . release_date . Year ! = 1 )
2013-11-23 20:01:44 +00:00
{
2013-12-29 18:53:56 +00:00
//no specific country release info at all
movie . PremiereDate = movieData . release_date . ToUniversalTime ( ) ;
movie . ProductionYear = movieData . release_date . Year ;
2013-11-23 20:01:44 +00:00
}
2013-05-03 04:10:11 +00:00
2013-12-26 14:20:17 +00:00
// If that didn't find a rating and we are a boxset, use the one from our first child
2013-11-23 20:01:44 +00:00
if ( movie . OfficialRating = = null & & movie is BoxSet & & ! movie . LockedFields . Contains ( MetadataFields . OfficialRating ) )
{
var boxset = movie as BoxSet ;
Logger . Info ( "MovieDbProvider - Using rating of first child of boxset..." ) ;
2013-05-03 04:10:11 +00:00
2013-11-23 20:01:44 +00:00
var firstChild = boxset . Children . Concat ( boxset . GetLinkedChildren ( ) ) . FirstOrDefault ( ) ;
2013-02-21 01:33:05 +00:00
2013-11-23 20:01:44 +00:00
boxset . OfficialRating = firstChild ! = null ? firstChild . OfficialRating : null ;
}
2013-02-21 01:33:05 +00:00
2013-11-23 20:01:44 +00:00
if ( movieData . runtime > 0 )
movie . OriginalRunTimeTicks = TimeSpan . FromMinutes ( movieData . runtime ) . Ticks ;
2013-04-28 05:44:45 +00:00
2013-11-23 20:01:44 +00:00
//studios
if ( movieData . production_companies ! = null & & ! movie . LockedFields . Contains ( MetadataFields . Studios ) )
{
movie . Studios . Clear ( ) ;
2013-02-21 01:33:05 +00:00
2013-11-23 20:01:44 +00:00
foreach ( var studio in movieData . production_companies . Select ( c = > c . name ) )
2013-02-21 01:33:05 +00:00
{
2013-11-23 20:01:44 +00:00
movie . AddStudio ( studio ) ;
2013-02-21 01:33:05 +00:00
}
2013-11-23 20:01:44 +00:00
}
2013-02-21 01:33:05 +00:00
2013-11-23 20:01:44 +00:00
// genres
// Movies get this from imdb
2013-12-27 00:23:58 +00:00
var genres = movieData . genres ? ? new List < GenreItem > ( ) ;
if ( ! movie . LockedFields . Contains ( MetadataFields . Genres ) )
2013-11-23 20:01:44 +00:00
{
// Only grab them if a boxset or there are no genres.
// For movies and trailers we'll use imdb via omdb
2013-12-27 00:23:58 +00:00
// But omdb data is for english users only so fetch if language is not english
if ( ! ( movie is Movie ) | | movie . Genres . Count = = 0 | | ! string . Equals ( movie . GetPreferredMetadataLanguage ( ) , "en" , StringComparison . OrdinalIgnoreCase ) )
2013-02-21 01:33:05 +00:00
{
2013-11-23 20:01:44 +00:00
movie . Genres . Clear ( ) ;
2013-07-29 12:38:58 +00:00
2013-12-27 00:23:58 +00:00
foreach ( var genre in genres . Select ( g = > g . name ) )
2013-07-29 12:38:58 +00:00
{
2013-11-23 20:01:44 +00:00
movie . AddGenre ( genre ) ;
2013-07-29 12:38:58 +00:00
}
2013-02-21 01:33:05 +00:00
}
2013-11-23 20:01:44 +00:00
}
2013-02-21 01:33:05 +00:00
2013-11-23 20:01:44 +00:00
if ( ! movie . LockedFields . Contains ( MetadataFields . Cast ) )
{
movie . People . Clear ( ) ;
//Actors, Directors, Writers - all in People
//actors come from cast
if ( movieData . casts ! = null & & movieData . casts . cast ! = null )
2013-05-06 22:52:13 +00:00
{
2013-11-23 20:01:44 +00:00
foreach ( var actor in movieData . casts . cast . OrderBy ( a = > a . order ) ) movie . AddPerson ( new PersonInfo { Name = actor . name . Trim ( ) , Role = actor . character , Type = PersonType . Actor , SortOrder = actor . order } ) ;
2013-05-06 22:52:13 +00:00
}
2013-06-27 16:36:41 +00:00
2013-11-23 20:01:44 +00:00
//and the rest from crew
if ( movieData . casts ! = null & & movieData . casts . crew ! = null )
2013-06-27 16:36:41 +00:00
{
2013-11-23 20:01:44 +00:00
foreach ( var person in movieData . casts . crew ) movie . AddPerson ( new PersonInfo { Name = person . name . Trim ( ) , Role = person . job , Type = person . department } ) ;
2013-06-27 16:36:41 +00:00
}
2013-02-21 01:33:05 +00:00
}
2014-01-02 21:21:06 +00:00
//if (movieData.keywords != null && movieData.keywords.keywords != null && !movie.LockedFields.Contains(MetadataFields.Tags))
//{
// var hasTags = movie as IHasTags;
// if (hasTags != null)
// {
// hasTags.Tags = movieData.keywords.keywords.Select(i => i.name).ToList();
// }
//}
2013-11-23 20:01:44 +00:00
if ( movieData . trailers ! = null & & movieData . trailers . youtube ! = null & &
movieData . trailers . youtube . Count > 0 )
{
2013-12-02 16:46:25 +00:00
var hasTrailers = movie as IHasTrailers ;
if ( hasTrailers ! = null )
2013-11-23 20:01:44 +00:00
{
2013-12-02 16:46:25 +00:00
hasTrailers . RemoteTrailers = movieData . trailers . youtube . Select ( i = > new MediaUrl
{
Url = string . Format ( "http://www.youtube.com/watch?v={0}" , i . source ) ,
IsDirectLink = false ,
Name = i . name ,
VideoSize = string . Equals ( "hd" , i . size , StringComparison . OrdinalIgnoreCase ) ? VideoSize . HighDefinition : VideoSize . StandardDefinition
2013-11-23 20:01:44 +00:00
2013-12-02 16:46:25 +00:00
} ) . ToList ( ) ;
}
2013-11-23 20:01:44 +00:00
}
2013-02-21 01:33:05 +00:00
}
2013-05-19 18:42:58 +00:00
private DateTime _lastRequestDate = DateTime . MinValue ;
/// <summary>
/// Gets the movie db response.
/// </summary>
internal async Task < Stream > GetMovieDbResponse ( HttpRequestOptions options )
{
var cancellationToken = options . CancellationToken ;
2013-10-13 17:52:57 +00:00
await MovieDbResourcePool . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
2013-05-19 18:42:58 +00:00
try
{
// Limit to three requests per second
2013-05-21 04:04:38 +00:00
var diff = 340 - ( DateTime . Now - _lastRequestDate ) . TotalMilliseconds ;
2013-05-19 18:42:58 +00:00
if ( diff > 0 )
{
await Task . Delay ( Convert . ToInt32 ( diff ) , cancellationToken ) . ConfigureAwait ( false ) ;
}
_lastRequestDate = DateTime . Now ;
return await HttpClient . Get ( options ) . ConfigureAwait ( false ) ;
}
finally
{
_lastRequestDate = DateTime . Now ;
2013-10-13 17:52:57 +00:00
MovieDbResourcePool . Release ( ) ;
2013-05-19 18:42:58 +00:00
}
}
2013-02-21 01:33:05 +00:00
2013-10-22 19:52:33 +00:00
public void Dispose ( )
{
Dispose ( true ) ;
}
2013-02-21 01:33:05 +00:00
2013-05-06 22:52:13 +00:00
/// <summary>
/// Class TmdbTitle
/// </summary>
2013-10-22 19:52:33 +00:00
internal class TmdbTitle
2013-05-06 22:52:13 +00:00
{
/// <summary>
/// Gets or sets the iso_3166_1.
/// </summary>
/// <value>The iso_3166_1.</value>
public string iso_3166_1 { get ; set ; }
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string title { get ; set ; }
}
/// <summary>
/// Class TmdbAltTitleResults
/// </summary>
2013-10-22 19:52:33 +00:00
internal class TmdbAltTitleResults
2013-05-06 22:52:13 +00:00
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public int id { get ; set ; }
/// <summary>
/// Gets or sets the titles.
/// </summary>
/// <value>The titles.</value>
public List < TmdbTitle > titles { get ; set ; }
}
2013-02-21 01:33:05 +00:00
/// <summary>
/// Class TmdbMovieSearchResult
/// </summary>
2013-10-22 19:52:33 +00:00
internal class TmdbMovieSearchResult
2013-02-21 01:33:05 +00:00
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="TmdbMovieSearchResult" /> is adult.
/// </summary>
/// <value><c>true</c> if adult; otherwise, <c>false</c>.</value>
public bool adult { get ; set ; }
/// <summary>
/// Gets or sets the backdrop_path.
/// </summary>
/// <value>The backdrop_path.</value>
public string backdrop_path { get ; set ; }
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public int id { get ; set ; }
/// <summary>
/// Gets or sets the original_title.
/// </summary>
/// <value>The original_title.</value>
public string original_title { get ; set ; }
/// <summary>
/// Gets or sets the release_date.
/// </summary>
/// <value>The release_date.</value>
public string release_date { get ; set ; }
/// <summary>
/// Gets or sets the poster_path.
/// </summary>
/// <value>The poster_path.</value>
public string poster_path { get ; set ; }
/// <summary>
/// Gets or sets the popularity.
/// </summary>
/// <value>The popularity.</value>
public double popularity { get ; set ; }
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string title { get ; set ; }
/// <summary>
/// Gets or sets the vote_average.
/// </summary>
/// <value>The vote_average.</value>
public double vote_average { get ; set ; }
/// <summary>
2013-08-26 14:36:41 +00:00
/// For collection search results
/// </summary>
public string name { get ; set ; }
/// <summary>
2013-02-21 01:33:05 +00:00
/// Gets or sets the vote_count.
/// </summary>
/// <value>The vote_count.</value>
public int vote_count { get ; set ; }
}
/// <summary>
/// Class TmdbMovieSearchResults
/// </summary>
2013-10-22 19:52:33 +00:00
internal class TmdbMovieSearchResults
2013-02-21 01:33:05 +00:00
{
/// <summary>
/// Gets or sets the page.
/// </summary>
/// <value>The page.</value>
public int page { get ; set ; }
/// <summary>
/// Gets or sets the results.
/// </summary>
/// <value>The results.</value>
public List < TmdbMovieSearchResult > results { get ; set ; }
/// <summary>
/// Gets or sets the total_pages.
/// </summary>
/// <value>The total_pages.</value>
public int total_pages { get ; set ; }
/// <summary>
/// Gets or sets the total_results.
/// </summary>
/// <value>The total_results.</value>
public int total_results { get ; set ; }
}
2013-06-28 20:25:58 +00:00
2013-10-22 19:52:33 +00:00
internal class BelongsToCollection
2013-02-21 01:33:05 +00:00
{
public int id { get ; set ; }
public string name { get ; set ; }
public string poster_path { get ; set ; }
public string backdrop_path { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class GenreItem
2013-02-21 01:33:05 +00:00
{
public int id { get ; set ; }
public string name { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class ProductionCompany
2013-02-21 01:33:05 +00:00
{
public string name { get ; set ; }
public int id { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class ProductionCountry
2013-02-21 01:33:05 +00:00
{
public string iso_3166_1 { get ; set ; }
public string name { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class SpokenLanguage
2013-02-21 01:33:05 +00:00
{
public string iso_639_1 { get ; set ; }
public string name { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class Cast
2013-02-21 01:33:05 +00:00
{
public int id { get ; set ; }
public string name { get ; set ; }
public string character { get ; set ; }
public int order { get ; set ; }
2013-05-06 22:52:13 +00:00
public int cast_id { get ; set ; }
2013-02-21 01:33:05 +00:00
public string profile_path { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class Crew
2013-02-21 01:33:05 +00:00
{
public int id { get ; set ; }
public string name { get ; set ; }
public string department { get ; set ; }
public string job { get ; set ; }
2013-05-06 22:52:13 +00:00
public string profile_path { get ; set ; }
2013-02-21 01:33:05 +00:00
}
2013-10-22 19:52:33 +00:00
internal class Casts
2013-02-21 01:33:05 +00:00
{
2013-05-06 22:52:13 +00:00
public List < Cast > cast { get ; set ; }
public List < Crew > crew { get ; set ; }
2013-02-21 01:33:05 +00:00
}
2013-10-22 19:52:33 +00:00
internal class Country
2013-02-21 01:33:05 +00:00
{
public string iso_3166_1 { get ; set ; }
2013-05-06 22:52:13 +00:00
public string certification { get ; set ; }
public DateTime release_date { get ; set ; }
2013-02-21 01:33:05 +00:00
}
2013-10-22 19:52:33 +00:00
internal class Releases
2013-02-21 01:33:05 +00:00
{
2013-05-06 22:52:13 +00:00
public List < Country > countries { get ; set ; }
2013-02-21 01:33:05 +00:00
}
2013-10-22 19:52:33 +00:00
internal class Backdrop
{
public string file_path { get ; set ; }
public int width { get ; set ; }
public int height { get ; set ; }
public object iso_639_1 { get ; set ; }
public double aspect_ratio { get ; set ; }
public double vote_average { get ; set ; }
public int vote_count { get ; set ; }
}
internal class Poster
{
public string file_path { get ; set ; }
public int width { get ; set ; }
public int height { get ; set ; }
public string iso_639_1 { get ; set ; }
public double aspect_ratio { get ; set ; }
public double vote_average { get ; set ; }
public int vote_count { get ; set ; }
}
internal class Images
{
public List < Backdrop > backdrops { get ; set ; }
public List < Poster > posters { get ; set ; }
}
internal class Keyword
2013-02-21 01:33:05 +00:00
{
public int id { get ; set ; }
2013-05-06 22:52:13 +00:00
public string name { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class Keywords
2013-05-06 22:52:13 +00:00
{
public List < Keyword > keywords { get ; set ; }
2013-02-21 01:33:05 +00:00
}
2013-10-22 19:52:33 +00:00
internal class Youtube
{
public string name { get ; set ; }
public string size { get ; set ; }
public string source { get ; set ; }
}
internal class Trailers
{
public List < object > quicktime { get ; set ; }
public List < Youtube > youtube { get ; set ; }
}
internal class CompleteMovieData
2013-02-21 01:33:05 +00:00
{
public bool adult { get ; set ; }
public string backdrop_path { get ; set ; }
public BelongsToCollection belongs_to_collection { get ; set ; }
public int budget { get ; set ; }
2013-05-06 22:52:13 +00:00
public List < GenreItem > genres { get ; set ; }
2013-02-21 01:33:05 +00:00
public string homepage { get ; set ; }
public int id { get ; set ; }
public string imdb_id { get ; set ; }
public string original_title { get ; set ; }
public string overview { get ; set ; }
public double popularity { get ; set ; }
public string poster_path { get ; set ; }
public List < ProductionCompany > production_companies { get ; set ; }
public List < ProductionCountry > production_countries { get ; set ; }
public DateTime release_date { get ; set ; }
public int revenue { get ; set ; }
public int runtime { get ; set ; }
public List < SpokenLanguage > spoken_languages { get ; set ; }
2013-05-06 22:52:13 +00:00
public string status { get ; set ; }
2013-02-21 01:33:05 +00:00
public string tagline { get ; set ; }
public string title { get ; set ; }
2013-10-22 19:52:33 +00:00
public string name { get ; set ; }
2013-02-21 01:33:05 +00:00
public double vote_average { get ; set ; }
public int vote_count { get ; set ; }
2013-05-06 22:52:13 +00:00
public Casts casts { get ; set ; }
public Releases releases { get ; set ; }
2013-10-22 19:52:33 +00:00
public Images images { get ; set ; }
2013-05-06 22:52:13 +00:00
public Keywords keywords { get ; set ; }
2013-06-27 16:36:41 +00:00
public Trailers trailers { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class TmdbImageSettings
2013-02-21 01:33:05 +00:00
{
public List < string > backdrop_sizes { get ; set ; }
public string base_url { get ; set ; }
public List < string > poster_sizes { get ; set ; }
public List < string > profile_sizes { get ; set ; }
}
2013-10-22 19:52:33 +00:00
internal class TmdbSettingsResult
2013-02-21 01:33:05 +00:00
{
public TmdbImageSettings images { get ; set ; }
}
}
}