TmdbUtils.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Text.RegularExpressions;
  5. using Jellyfin.Data.Enums;
  6. using MediaBrowser.Model.Entities;
  7. using TMDbLib.Objects.General;
  8. namespace MediaBrowser.Providers.Plugins.Tmdb
  9. {
  10. /// <summary>
  11. /// Utilities for the TMDb provider.
  12. /// </summary>
  13. public static partial class TmdbUtils
  14. {
  15. /// <summary>
  16. /// URL of the TMDb instance to use.
  17. /// </summary>
  18. public const string BaseTmdbUrl = "https://www.themoviedb.org/";
  19. /// <summary>
  20. /// Name of the provider.
  21. /// </summary>
  22. public const string ProviderName = "TheMovieDb";
  23. /// <summary>
  24. /// API key to use when performing an API call.
  25. /// </summary>
  26. public const string ApiKey = "4219e299c89411838049ab0dab19ebd5";
  27. /// <summary>
  28. /// The crew types to keep.
  29. /// </summary>
  30. public static readonly string[] WantedCrewTypes =
  31. {
  32. PersonType.Director,
  33. PersonType.Writer,
  34. PersonType.Producer
  35. };
  36. /// <summary>
  37. /// The crew kinds to keep.
  38. /// </summary>
  39. public static readonly PersonKind[] WantedCrewKinds =
  40. {
  41. PersonKind.Director,
  42. PersonKind.Writer,
  43. PersonKind.Producer
  44. };
  45. [GeneratedRegex(@"[\W_]+")]
  46. private static partial Regex NonWordRegex();
  47. /// <summary>
  48. /// Cleans the name according to TMDb requirements.
  49. /// </summary>
  50. /// <param name="name">The name of the entity.</param>
  51. /// <returns>The cleaned name.</returns>
  52. public static string CleanName(string name)
  53. {
  54. // TMDb expects a space separated list of words make sure that is the case
  55. return NonWordRegex().Replace(name, " ");
  56. }
  57. /// <summary>
  58. /// Maps the TMDb provided roles for crew members to Jellyfin roles.
  59. /// </summary>
  60. /// <param name="crew">Crew member to map against the Jellyfin person types.</param>
  61. /// <returns>The Jellyfin person type.</returns>
  62. public static PersonKind MapCrewToPersonType(Crew crew)
  63. {
  64. if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase)
  65. && crew.Job.Contains("director", StringComparison.OrdinalIgnoreCase))
  66. {
  67. return PersonKind.Director;
  68. }
  69. if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase)
  70. && crew.Job.Contains("producer", StringComparison.OrdinalIgnoreCase))
  71. {
  72. return PersonKind.Producer;
  73. }
  74. if (crew.Department.Equals("writing", StringComparison.OrdinalIgnoreCase))
  75. {
  76. return PersonKind.Writer;
  77. }
  78. return PersonKind.Unknown;
  79. }
  80. /// <summary>
  81. /// Determines whether a video is a trailer.
  82. /// </summary>
  83. /// <param name="video">The TMDb video.</param>
  84. /// <returns>A boolean indicating whether the video is a trailer.</returns>
  85. public static bool IsTrailerType(Video video)
  86. {
  87. return video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)
  88. && (video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
  89. || video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
  90. }
  91. /// <summary>
  92. /// Normalizes a language string for use with TMDb's include image language parameter.
  93. /// </summary>
  94. /// <param name="preferredLanguage">The preferred language as either a 2 letter code with or without country code.</param>
  95. /// <returns>The comma separated language string.</returns>
  96. public static string GetImageLanguagesParam(string preferredLanguage)
  97. {
  98. var languages = new List<string>();
  99. if (!string.IsNullOrEmpty(preferredLanguage))
  100. {
  101. preferredLanguage = NormalizeLanguage(preferredLanguage);
  102. languages.Add(preferredLanguage);
  103. if (preferredLanguage.Length == 5) // Like en-US
  104. {
  105. // Currently, TMDb supports 2-letter language codes only.
  106. // They are planning to change this in the future, thus we're
  107. // supplying both codes if we're having a 5-letter code.
  108. languages.Add(preferredLanguage.Substring(0, 2));
  109. }
  110. }
  111. languages.Add("null");
  112. // Always add English as fallback language
  113. if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase))
  114. {
  115. languages.Add("en");
  116. }
  117. return string.Join(',', languages);
  118. }
  119. /// <summary>
  120. /// Normalizes a language string for use with TMDb's language parameter.
  121. /// </summary>
  122. /// <param name="language">The language code.</param>
  123. /// <returns>The normalized language code.</returns>
  124. [return: NotNullIfNotNull(nameof(language))]
  125. public static string? NormalizeLanguage(string? language)
  126. {
  127. if (string.IsNullOrEmpty(language))
  128. {
  129. return language;
  130. }
  131. // TMDb requires this to be uppercase
  132. // Everything after the hyphen must be written in uppercase due to a way TMDb wrote their API.
  133. // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab
  134. var parts = language.Split('-');
  135. if (parts.Length == 2)
  136. {
  137. // TMDb doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without country code
  138. if (string.Equals(parts[1], "CH", StringComparison.OrdinalIgnoreCase))
  139. {
  140. return parts[0];
  141. }
  142. language = parts[0] + "-" + parts[1].ToUpperInvariant();
  143. }
  144. return language;
  145. }
  146. /// <summary>
  147. /// Adjusts the image's language code preferring the 5 letter language code eg. en-US.
  148. /// </summary>
  149. /// <param name="imageLanguage">The image's actual language code.</param>
  150. /// <param name="requestLanguage">The requested language code.</param>
  151. /// <returns>The language code.</returns>
  152. public static string AdjustImageLanguage(string imageLanguage, string requestLanguage)
  153. {
  154. if (!string.IsNullOrEmpty(imageLanguage)
  155. && !string.IsNullOrEmpty(requestLanguage)
  156. && requestLanguage.Length > 2
  157. && imageLanguage.Length == 2
  158. && requestLanguage.StartsWith(imageLanguage, StringComparison.OrdinalIgnoreCase))
  159. {
  160. return requestLanguage;
  161. }
  162. return imageLanguage;
  163. }
  164. /// <summary>
  165. /// Combines the metadata country code and the parental rating from the API into the value we store in our database.
  166. /// </summary>
  167. /// <param name="countryCode">The ISO 3166-1 country code of the rating country.</param>
  168. /// <param name="ratingValue">The rating value returned by the TMDb API.</param>
  169. /// <returns>The combined parental rating of country code+rating value.</returns>
  170. public static string BuildParentalRating(string countryCode, string ratingValue)
  171. {
  172. // Exclude US because we store US values as TV-14 without the country code.
  173. var ratingPrefix = string.Equals(countryCode, "US", StringComparison.OrdinalIgnoreCase) ? string.Empty : countryCode + "-";
  174. var newRating = ratingPrefix + ratingValue;
  175. return newRating.Replace("DE-", "FSK-", StringComparison.OrdinalIgnoreCase);
  176. }
  177. }
  178. }