MovieDbSeasonProvider.cs 12 KB

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