MovieDbSeasonProvider.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. using MediaBrowser.Common.IO;
  2. using MediaBrowser.Common.Net;
  3. using MediaBrowser.Controller.Configuration;
  4. using MediaBrowser.Controller.Entities.TV;
  5. using MediaBrowser.Controller.Localization;
  6. using MediaBrowser.Controller.Providers;
  7. using MediaBrowser.Model.Entities;
  8. using MediaBrowser.Model.Logging;
  9. using MediaBrowser.Model.Net;
  10. using MediaBrowser.Model.Providers;
  11. using MediaBrowser.Model.Serialization;
  12. using MediaBrowser.Providers.Movies;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Globalization;
  16. using System.IO;
  17. using System.Linq;
  18. using System.Net;
  19. using System.Threading;
  20. using System.Threading.Tasks;
  21. namespace MediaBrowser.Providers.TV
  22. {
  23. public class MovieDbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo>
  24. {
  25. private const string GetTvInfo3 = @"http://api.themoviedb.org/3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos";
  26. private readonly IHttpClient _httpClient;
  27. private readonly IServerConfigurationManager _configurationManager;
  28. private readonly IJsonSerializer _jsonSerializer;
  29. private readonly IFileSystem _fileSystem;
  30. private readonly ILocalizationManager _localization;
  31. private readonly ILogger _logger;
  32. public MovieDbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILogManager logManager)
  33. {
  34. _httpClient = httpClient;
  35. _configurationManager = configurationManager;
  36. _fileSystem = fileSystem;
  37. _localization = localization;
  38. _jsonSerializer = jsonSerializer;
  39. _logger = logManager.GetLogger(GetType().Name);
  40. }
  41. public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, CancellationToken cancellationToken)
  42. {
  43. var result = new MetadataResult<Season>();
  44. string seriesTmdbId;
  45. info.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out seriesTmdbId);
  46. var seasonNumber = info.IndexNumber;
  47. if (!string.IsNullOrWhiteSpace(seriesTmdbId) && seasonNumber.HasValue)
  48. {
  49. try
  50. {
  51. var seasonInfo = await GetSeasonInfo(seriesTmdbId, seasonNumber.Value, info.MetadataLanguage, cancellationToken)
  52. .ConfigureAwait(false);
  53. result.HasMetadata = true;
  54. result.Item = new Season();
  55. result.Item.Name = info.Name;
  56. result.Item.IndexNumber = seasonNumber;
  57. result.Item.Overview = seasonInfo.overview;
  58. if (seasonInfo.external_ids.tvdb_id > 0)
  59. {
  60. result.Item.SetProviderId(MetadataProviders.Tvdb, seasonInfo.external_ids.tvdb_id.ToString(CultureInfo.InvariantCulture));
  61. }
  62. var credits = seasonInfo.credits;
  63. if (credits != null)
  64. {
  65. //Actors, Directors, Writers - all in People
  66. //actors come from cast
  67. if (credits.cast != null)
  68. {
  69. //foreach (var actor in credits.cast.OrderBy(a => a.order)) result.Item.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order });
  70. }
  71. //and the rest from crew
  72. if (credits.crew != null)
  73. {
  74. //foreach (var person in credits.crew) result.Item.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department });
  75. }
  76. }
  77. result.Item.PremiereDate = seasonInfo.air_date;
  78. result.Item.ProductionYear = result.Item.PremiereDate.Value.Year;
  79. }
  80. catch (HttpException ex)
  81. {
  82. _logger.Error("No metadata found for {0}", seasonNumber.Value);
  83. if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
  84. {
  85. return result;
  86. }
  87. throw;
  88. }
  89. }
  90. return result;
  91. }
  92. public string Name
  93. {
  94. get { return "TheMovieDb"; }
  95. }
  96. public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeasonInfo searchInfo, CancellationToken cancellationToken)
  97. {
  98. return Task.FromResult<IEnumerable<RemoteSearchResult>>(new List<RemoteSearchResult>());
  99. }
  100. public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
  101. {
  102. return _httpClient.GetResponse(new HttpRequestOptions
  103. {
  104. CancellationToken = cancellationToken,
  105. Url = url,
  106. ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
  107. });
  108. }
  109. private async Task<RootObject> GetSeasonInfo(string seriesTmdbId, int season, string preferredMetadataLanguage,
  110. CancellationToken cancellationToken)
  111. {
  112. await EnsureSeasonInfo(seriesTmdbId, season, preferredMetadataLanguage, cancellationToken)
  113. .ConfigureAwait(false);
  114. var dataFilePath = GetDataFilePath(seriesTmdbId, season, preferredMetadataLanguage);
  115. return _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath);
  116. }
  117. private readonly Task _cachedTask = Task.FromResult(true);
  118. internal Task EnsureSeasonInfo(string tmdbId, int seasonNumber, string language, CancellationToken cancellationToken)
  119. {
  120. if (string.IsNullOrEmpty(tmdbId))
  121. {
  122. throw new ArgumentNullException("tmdbId");
  123. }
  124. if (string.IsNullOrEmpty(language))
  125. {
  126. throw new ArgumentNullException("language");
  127. }
  128. var path = GetDataFilePath(tmdbId, seasonNumber, language);
  129. var fileInfo = _fileSystem.GetFileSystemInfo(path);
  130. if (fileInfo.Exists)
  131. {
  132. // If it's recent or automatic updates are enabled, don't re-download
  133. if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
  134. {
  135. return _cachedTask;
  136. }
  137. }
  138. return DownloadSeasonInfo(tmdbId, seasonNumber, language, cancellationToken);
  139. }
  140. internal string GetDataFilePath(string tmdbId, int seasonNumber, string preferredLanguage)
  141. {
  142. if (string.IsNullOrEmpty(tmdbId))
  143. {
  144. throw new ArgumentNullException("tmdbId");
  145. }
  146. if (string.IsNullOrEmpty(preferredLanguage))
  147. {
  148. throw new ArgumentNullException("preferredLanguage");
  149. }
  150. var path = MovieDbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId);
  151. var filename = string.Format("season-{0}-{1}.json",
  152. seasonNumber.ToString(CultureInfo.InvariantCulture),
  153. preferredLanguage);
  154. return Path.Combine(path, filename);
  155. }
  156. internal async Task DownloadSeasonInfo(string id, int seasonNumber, string preferredMetadataLanguage, CancellationToken cancellationToken)
  157. {
  158. var mainResult = await FetchMainResult(id, seasonNumber, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
  159. var dataFilePath = GetDataFilePath(id, seasonNumber, preferredMetadataLanguage);
  160. Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
  161. _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
  162. }
  163. internal async Task<RootObject> FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken)
  164. {
  165. var url = string.Format(GetTvInfo3, id, seasonNumber.ToString(CultureInfo.InvariantCulture), MovieDbProvider.ApiKey);
  166. if (!string.IsNullOrEmpty(language))
  167. {
  168. url += string.Format("&language={0}", language);
  169. }
  170. var includeImageLanguageParam = MovieDbProvider.GetImageLanguagesParam(_localization, language);
  171. // Get images in english and with no language
  172. url += "&include_image_language=" + includeImageLanguageParam;
  173. cancellationToken.ThrowIfCancellationRequested();
  174. using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
  175. {
  176. Url = url,
  177. CancellationToken = cancellationToken,
  178. AcceptHeader = MovieDbProvider.AcceptHeader
  179. }).ConfigureAwait(false))
  180. {
  181. return _jsonSerializer.DeserializeFromStream<RootObject>(json);
  182. }
  183. }
  184. public class Episode
  185. {
  186. public string air_date { get; set; }
  187. public int episode_number { get; set; }
  188. public int id { get; set; }
  189. public string name { get; set; }
  190. public string overview { get; set; }
  191. public string still_path { get; set; }
  192. public double vote_average { get; set; }
  193. public int vote_count { get; set; }
  194. }
  195. public class Cast
  196. {
  197. public string character { get; set; }
  198. public string credit_id { get; set; }
  199. public int id { get; set; }
  200. public string name { get; set; }
  201. public string profile_path { get; set; }
  202. public int order { get; set; }
  203. }
  204. public class Crew
  205. {
  206. public string credit_id { get; set; }
  207. public string department { get; set; }
  208. public int id { get; set; }
  209. public string name { get; set; }
  210. public string job { get; set; }
  211. public string profile_path { get; set; }
  212. }
  213. public class Credits
  214. {
  215. public List<Cast> cast { get; set; }
  216. public List<Crew> crew { get; set; }
  217. }
  218. public class Poster
  219. {
  220. public double aspect_ratio { get; set; }
  221. public string file_path { get; set; }
  222. public int height { get; set; }
  223. public string id { get; set; }
  224. public string iso_639_1 { get; set; }
  225. public double vote_average { get; set; }
  226. public int vote_count { get; set; }
  227. public int width { get; set; }
  228. }
  229. public class Images
  230. {
  231. public List<Poster> posters { get; set; }
  232. }
  233. public class ExternalIds
  234. {
  235. public string freebase_id { get; set; }
  236. public string freebase_mid { get; set; }
  237. public int tvdb_id { get; set; }
  238. public object tvrage_id { get; set; }
  239. }
  240. public class Videos
  241. {
  242. public List<object> results { get; set; }
  243. }
  244. public class RootObject
  245. {
  246. public DateTime air_date { get; set; }
  247. public List<Episode> episodes { get; set; }
  248. public string name { get; set; }
  249. public string overview { get; set; }
  250. public int id { get; set; }
  251. public string poster_path { get; set; }
  252. public int season_number { get; set; }
  253. public Credits credits { get; set; }
  254. public Images images { get; set; }
  255. public ExternalIds external_ids { get; set; }
  256. public Videos videos { get; set; }
  257. }
  258. }
  259. }