ManualFanartSeriesProvider.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. using MediaBrowser.Controller.Configuration;
  2. using MediaBrowser.Controller.Entities;
  3. using MediaBrowser.Controller.Entities.TV;
  4. using MediaBrowser.Controller.Providers;
  5. using MediaBrowser.Model.Dto;
  6. using MediaBrowser.Model.Entities;
  7. using MediaBrowser.Model.Providers;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Globalization;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using System.Xml;
  17. namespace MediaBrowser.Providers.TV
  18. {
  19. public class ManualFanartSeriesImageProvider : IImageProvider
  20. {
  21. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  22. private readonly IServerConfigurationManager _config;
  23. public ManualFanartSeriesImageProvider(IServerConfigurationManager config)
  24. {
  25. _config = config;
  26. }
  27. public string Name
  28. {
  29. get { return ProviderName; }
  30. }
  31. public static string ProviderName
  32. {
  33. get { return "FanArt"; }
  34. }
  35. public bool Supports(IHasImages item)
  36. {
  37. return item is Series;
  38. }
  39. public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
  40. {
  41. var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
  42. return images.Where(i => i.Type == imageType);
  43. }
  44. public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
  45. {
  46. var list = new List<RemoteImageInfo>();
  47. var series = (Series)item;
  48. var id = series.GetProviderId(MetadataProviders.Tvdb);
  49. if (!string.IsNullOrEmpty(id))
  50. {
  51. var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id);
  52. try
  53. {
  54. AddImages(list, xmlPath, cancellationToken);
  55. }
  56. catch (FileNotFoundException)
  57. {
  58. // No biggie. Don't blow up
  59. }
  60. }
  61. var language = item.GetPreferredMetadataLanguage();
  62. var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
  63. // Sort first by width to prioritize HD versions
  64. list = list.OrderByDescending(i => i.Width ?? 0)
  65. .ThenByDescending(i =>
  66. {
  67. if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
  68. {
  69. return 3;
  70. }
  71. if (!isLanguageEn)
  72. {
  73. if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
  74. {
  75. return 2;
  76. }
  77. }
  78. if (string.IsNullOrEmpty(i.Language))
  79. {
  80. return isLanguageEn ? 3 : 2;
  81. }
  82. return 0;
  83. })
  84. .ThenByDescending(i => i.CommunityRating ?? 0)
  85. .ThenByDescending(i => i.VoteCount ?? 0)
  86. .ToList();
  87. return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
  88. }
  89. private void AddImages(List<RemoteImageInfo> list, string xmlPath, CancellationToken cancellationToken)
  90. {
  91. using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
  92. {
  93. // Use XmlReader for best performance
  94. using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings
  95. {
  96. CheckCharacters = false,
  97. IgnoreProcessingInstructions = true,
  98. IgnoreComments = true,
  99. ValidationType = ValidationType.None
  100. }))
  101. {
  102. reader.MoveToContent();
  103. // Loop through each element
  104. while (reader.Read())
  105. {
  106. cancellationToken.ThrowIfCancellationRequested();
  107. if (reader.NodeType == XmlNodeType.Element)
  108. {
  109. switch (reader.Name)
  110. {
  111. case "series":
  112. {
  113. using (var subReader = reader.ReadSubtree())
  114. {
  115. AddImages(list, subReader, cancellationToken);
  116. }
  117. break;
  118. }
  119. default:
  120. reader.Skip();
  121. break;
  122. }
  123. }
  124. }
  125. }
  126. }
  127. }
  128. private void AddImages(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken)
  129. {
  130. reader.MoveToContent();
  131. while (reader.Read())
  132. {
  133. if (reader.NodeType == XmlNodeType.Element)
  134. {
  135. switch (reader.Name)
  136. {
  137. case "hdtvlogos":
  138. {
  139. using (var subReader = reader.ReadSubtree())
  140. {
  141. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 800, 310);
  142. }
  143. break;
  144. }
  145. case "hdcleararts":
  146. {
  147. using (var subReader = reader.ReadSubtree())
  148. {
  149. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 1000, 562);
  150. }
  151. break;
  152. }
  153. case "clearlogos":
  154. {
  155. using (var subReader = reader.ReadSubtree())
  156. {
  157. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 400, 155);
  158. }
  159. break;
  160. }
  161. case "cleararts":
  162. {
  163. using (var subReader = reader.ReadSubtree())
  164. {
  165. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 500, 281);
  166. }
  167. break;
  168. }
  169. case "showbackgrounds":
  170. {
  171. using (var subReader = reader.ReadSubtree())
  172. {
  173. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080, true);
  174. }
  175. break;
  176. }
  177. case "seasonthumbs":
  178. {
  179. using (var subReader = reader.ReadSubtree())
  180. {
  181. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281);
  182. }
  183. break;
  184. }
  185. case "tvthumbs":
  186. {
  187. using (var subReader = reader.ReadSubtree())
  188. {
  189. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281);
  190. }
  191. break;
  192. }
  193. case "tvbanners":
  194. {
  195. using (var subReader = reader.ReadSubtree())
  196. {
  197. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Banner, 1000, 185);
  198. }
  199. break;
  200. }
  201. case "tvposters":
  202. {
  203. using (var subReader = reader.ReadSubtree())
  204. {
  205. PopulateImageCategory(list, subReader, cancellationToken, ImageType.Primary, 1000, 1426);
  206. }
  207. break;
  208. }
  209. default:
  210. {
  211. using (reader.ReadSubtree())
  212. {
  213. }
  214. break;
  215. }
  216. }
  217. }
  218. }
  219. }
  220. private void PopulateImageCategory(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height, bool allowSeasonAll = false)
  221. {
  222. reader.MoveToContent();
  223. while (reader.Read())
  224. {
  225. cancellationToken.ThrowIfCancellationRequested();
  226. if (reader.NodeType == XmlNodeType.Element)
  227. {
  228. switch (reader.Name)
  229. {
  230. case "hdtvlogo":
  231. case "hdclearart":
  232. case "clearlogo":
  233. case "clearart":
  234. case "showbackground":
  235. case "seasonthumb":
  236. case "tvthumb":
  237. case "tvbanner":
  238. case "tvposter":
  239. {
  240. var url = reader.GetAttribute("url");
  241. var season = reader.GetAttribute("season");
  242. var isSeasonValid = string.IsNullOrEmpty(season) ||
  243. (allowSeasonAll && string.Equals(season, "all", StringComparison.OrdinalIgnoreCase));
  244. if (!string.IsNullOrEmpty(url) && isSeasonValid)
  245. {
  246. var likesString = reader.GetAttribute("likes");
  247. int likes;
  248. var info = new RemoteImageInfo
  249. {
  250. RatingType = RatingType.Likes,
  251. Type = type,
  252. Width = width,
  253. Height = height,
  254. ProviderName = Name,
  255. Url = url,
  256. Language = reader.GetAttribute("lang")
  257. };
  258. if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes))
  259. {
  260. info.CommunityRating = likes;
  261. }
  262. list.Add(info);
  263. }
  264. break;
  265. }
  266. default:
  267. reader.Skip();
  268. break;
  269. }
  270. }
  271. }
  272. }
  273. public int Priority
  274. {
  275. get { return 0; }
  276. }
  277. }
  278. }