GenericMovieDbInfo.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. using MediaBrowser.Controller.Entities;
  2. using MediaBrowser.Controller.Entities.Movies;
  3. using MediaBrowser.Controller.Library;
  4. using MediaBrowser.Controller.Providers;
  5. using MediaBrowser.Model.Entities;
  6. using MediaBrowser.Model.Logging;
  7. using MediaBrowser.Model.Serialization;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Globalization;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Net;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using CommonIO;
  17. using MediaBrowser.Common.IO;
  18. namespace MediaBrowser.Providers.Movies
  19. {
  20. public class GenericMovieDbInfo<T>
  21. where T : BaseItem, new()
  22. {
  23. private readonly ILogger _logger;
  24. private readonly IJsonSerializer _jsonSerializer;
  25. private readonly ILibraryManager _libraryManager;
  26. private readonly IFileSystem _fileSystem;
  27. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  28. public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem)
  29. {
  30. _logger = logger;
  31. _jsonSerializer = jsonSerializer;
  32. _libraryManager = libraryManager;
  33. _fileSystem = fileSystem;
  34. }
  35. public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken)
  36. {
  37. var tmdbId = itemId.GetProviderId(MetadataProviders.Tmdb);
  38. var imdbId = itemId.GetProviderId(MetadataProviders.Imdb);
  39. // Don't search for music video id's because it is very easy to misidentify.
  40. if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId) && typeof(T) != typeof(MusicVideo))
  41. {
  42. var searchResults = await new MovieDbSearch(_logger, _jsonSerializer, _libraryManager).GetMovieSearchResults(itemId, cancellationToken).ConfigureAwait(false);
  43. var searchResult = searchResults.FirstOrDefault();
  44. if (searchResult != null)
  45. {
  46. tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
  47. }
  48. }
  49. if (!string.IsNullOrEmpty(tmdbId) || !string.IsNullOrEmpty(imdbId))
  50. {
  51. cancellationToken.ThrowIfCancellationRequested();
  52. return await FetchMovieData(tmdbId, imdbId, itemId.MetadataLanguage, itemId.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
  53. }
  54. return new MetadataResult<T>();
  55. }
  56. /// <summary>
  57. /// Fetches the movie data.
  58. /// </summary>
  59. /// <param name="tmdbId">The TMDB identifier.</param>
  60. /// <param name="imdbId">The imdb identifier.</param>
  61. /// <param name="language">The language.</param>
  62. /// <param name="preferredCountryCode">The preferred country code.</param>
  63. /// <param name="cancellationToken">The cancellation token.</param>
  64. /// <returns>Task{`0}.</returns>
  65. private async Task<MetadataResult<T>> FetchMovieData(string tmdbId, string imdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
  66. {
  67. var item = new MetadataResult<T>
  68. {
  69. Item = new T()
  70. };
  71. string dataFilePath = null;
  72. MovieDbProvider.CompleteMovieData movieInfo = null;
  73. // Id could be ImdbId or TmdbId
  74. if (string.IsNullOrEmpty(tmdbId))
  75. {
  76. movieInfo = await MovieDbProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false);
  77. if (movieInfo != null)
  78. {
  79. tmdbId = movieInfo.id.ToString(_usCulture);
  80. dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
  81. _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
  82. _jsonSerializer.SerializeToFile(movieInfo, dataFilePath);
  83. }
  84. }
  85. if (!string.IsNullOrWhiteSpace(tmdbId))
  86. {
  87. await MovieDbProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
  88. dataFilePath = dataFilePath ?? MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
  89. movieInfo = movieInfo ?? _jsonSerializer.DeserializeFromFile<MovieDbProvider.CompleteMovieData>(dataFilePath);
  90. var settings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
  91. ProcessMainInfo(item, settings, preferredCountryCode, movieInfo);
  92. item.HasMetadata = true;
  93. }
  94. return item;
  95. }
  96. /// <summary>
  97. /// Processes the main info.
  98. /// </summary>
  99. /// <param name="resultItem">The result item.</param>
  100. /// <param name="settings">The settings.</param>
  101. /// <param name="preferredCountryCode">The preferred country code.</param>
  102. /// <param name="movieData">The movie data.</param>
  103. private void ProcessMainInfo(MetadataResult<T> resultItem, TmdbSettingsResult settings, string preferredCountryCode, MovieDbProvider.CompleteMovieData movieData)
  104. {
  105. var movie = resultItem.Item;
  106. movie.Name = movieData.GetTitle() ?? movie.Name;
  107. var hasOriginalTitle = movie as IHasOriginalTitle;
  108. if (hasOriginalTitle != null)
  109. {
  110. hasOriginalTitle.OriginalTitle = movieData.GetOriginalTitle();
  111. }
  112. // Bug in Mono: WebUtility.HtmlDecode should return null if the string is null but in Mono it generate an System.ArgumentNullException.
  113. movie.Overview = movieData.overview != null ? WebUtility.HtmlDecode(movieData.overview) : null;
  114. movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null;
  115. movie.HomePageUrl = movieData.homepage;
  116. var hasBudget = movie as IHasBudget;
  117. if (hasBudget != null)
  118. {
  119. hasBudget.Budget = movieData.budget;
  120. hasBudget.Revenue = movieData.revenue;
  121. }
  122. if (!string.IsNullOrEmpty(movieData.tagline))
  123. {
  124. var hasTagline = movie as IHasTaglines;
  125. if (hasTagline != null)
  126. {
  127. hasTagline.Taglines.Clear();
  128. hasTagline.AddTagline(movieData.tagline);
  129. }
  130. }
  131. if (movieData.production_countries != null)
  132. {
  133. var hasProductionLocations = movie as IHasProductionLocations;
  134. if (hasProductionLocations != null)
  135. {
  136. hasProductionLocations.ProductionLocations = movieData
  137. .production_countries
  138. .Select(i => i.name)
  139. .ToList();
  140. }
  141. }
  142. movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));
  143. movie.SetProviderId(MetadataProviders.Imdb, movieData.imdb_id);
  144. if (movieData.belongs_to_collection != null)
  145. {
  146. movie.SetProviderId(MetadataProviders.TmdbCollection,
  147. movieData.belongs_to_collection.id.ToString(CultureInfo.InvariantCulture));
  148. var movieItem = movie as Movie;
  149. if (movieItem != null)
  150. {
  151. movieItem.TmdbCollectionName = movieData.belongs_to_collection.name;
  152. }
  153. }
  154. float rating;
  155. string voteAvg = movieData.vote_average.ToString(CultureInfo.InvariantCulture);
  156. if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out rating))
  157. {
  158. movie.CommunityRating = rating;
  159. }
  160. movie.VoteCount = movieData.vote_count;
  161. //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
  162. if (movieData.releases != null && movieData.releases.countries != null)
  163. {
  164. var releases = movieData.releases.countries.Where(i => !string.IsNullOrWhiteSpace(i.certification)).ToList();
  165. var ourRelease = releases.FirstOrDefault(c => c.iso_3166_1.Equals(preferredCountryCode, StringComparison.OrdinalIgnoreCase));
  166. var usRelease = releases.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase));
  167. var minimunRelease = releases.OrderBy(c => c.release_date).FirstOrDefault();
  168. if (ourRelease != null)
  169. {
  170. var ratingPrefix = string.Equals(preferredCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? "" : preferredCountryCode + "-";
  171. movie.OfficialRating = ratingPrefix + ourRelease.certification;
  172. }
  173. else if (usRelease != null)
  174. {
  175. movie.OfficialRating = usRelease.certification;
  176. }
  177. else if (minimunRelease != null)
  178. {
  179. movie.OfficialRating = minimunRelease.iso_3166_1 + "-" + minimunRelease.certification;
  180. }
  181. }
  182. if (!string.IsNullOrWhiteSpace(movieData.release_date))
  183. {
  184. DateTime r;
  185. // These dates are always in this exact format
  186. if (DateTime.TryParse(movieData.release_date, _usCulture, DateTimeStyles.None, out r))
  187. {
  188. movie.PremiereDate = r.ToUniversalTime();
  189. movie.ProductionYear = movie.PremiereDate.Value.Year;
  190. }
  191. }
  192. //studios
  193. if (movieData.production_companies != null)
  194. {
  195. movie.Studios.Clear();
  196. foreach (var studio in movieData.production_companies.Select(c => c.name))
  197. {
  198. movie.AddStudio(studio);
  199. }
  200. }
  201. // genres
  202. // Movies get this from imdb
  203. var genres = movieData.genres ?? new List<MovieDbProvider.GenreItem>();
  204. foreach (var genre in genres.Select(g => g.name))
  205. {
  206. movie.AddGenre(genre);
  207. }
  208. resultItem.ResetPeople();
  209. var tmdbImageUrl = settings.images.base_url + "original";
  210. //Actors, Directors, Writers - all in People
  211. //actors come from cast
  212. if (movieData.casts != null && movieData.casts.cast != null)
  213. {
  214. foreach (var actor in movieData.casts.cast.OrderBy(a => a.order))
  215. {
  216. var personInfo = new PersonInfo
  217. {
  218. Name = actor.name.Trim(),
  219. Role = actor.character,
  220. Type = PersonType.Actor,
  221. SortOrder = actor.order
  222. };
  223. if (!string.IsNullOrWhiteSpace(actor.profile_path))
  224. {
  225. personInfo.ImageUrl = tmdbImageUrl + actor.profile_path;
  226. }
  227. if (actor.id > 0)
  228. {
  229. personInfo.SetProviderId(MetadataProviders.Tmdb, actor.id.ToString(CultureInfo.InvariantCulture));
  230. }
  231. resultItem.AddPerson(personInfo);
  232. }
  233. }
  234. //and the rest from crew
  235. if (movieData.casts != null && movieData.casts.crew != null)
  236. {
  237. foreach (var person in movieData.casts.crew)
  238. {
  239. // Normalize this
  240. var type = person.department;
  241. if (string.Equals(type, "writing", StringComparison.OrdinalIgnoreCase))
  242. {
  243. type = PersonType.Writer;
  244. }
  245. var personInfo = new PersonInfo
  246. {
  247. Name = person.name.Trim(),
  248. Role = person.job,
  249. Type = type
  250. };
  251. if (!string.IsNullOrWhiteSpace(person.profile_path))
  252. {
  253. personInfo.ImageUrl = tmdbImageUrl + person.profile_path;
  254. }
  255. if (person.id > 0)
  256. {
  257. personInfo.SetProviderId(MetadataProviders.Tmdb, person.id.ToString(CultureInfo.InvariantCulture));
  258. }
  259. resultItem.AddPerson(personInfo);
  260. }
  261. }
  262. if (movieData.keywords != null && movieData.keywords.keywords != null)
  263. {
  264. var hasTags = movie as IHasKeywords;
  265. if (hasTags != null)
  266. {
  267. hasTags.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList();
  268. }
  269. }
  270. if (movieData.trailers != null && movieData.trailers.youtube != null &&
  271. movieData.trailers.youtube.Count > 0)
  272. {
  273. var hasTrailers = movie as IHasTrailers;
  274. if (hasTrailers != null)
  275. {
  276. hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
  277. {
  278. Url = string.Format("http://www.youtube.com/watch?v={0}", i.source),
  279. Name = i.name,
  280. VideoSize = string.Equals("hd", i.size, StringComparison.OrdinalIgnoreCase) ? VideoSize.HighDefinition : VideoSize.StandardDefinition
  281. }).ToList();
  282. }
  283. }
  284. }
  285. }
  286. }