PlaylistImageEnhancer.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. using MediaBrowser.Common.IO;
  2. using MediaBrowser.Controller.Entities;
  3. using MediaBrowser.Controller.Entities.Audio;
  4. using MediaBrowser.Controller.Entities.TV;
  5. using MediaBrowser.Controller.Playlists;
  6. using MediaBrowser.Controller.Providers;
  7. using MediaBrowser.Model.Drawing;
  8. using MediaBrowser.Model.Entities;
  9. using MoreLinq;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Drawing;
  13. using System.Drawing.Drawing2D;
  14. using System.Drawing.Imaging;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Threading.Tasks;
  18. namespace MediaBrowser.Server.Implementations.Playlists
  19. {
  20. public class PlaylistImageEnhancer : IImageEnhancer
  21. {
  22. private readonly IFileSystem _fileSystem;
  23. public PlaylistImageEnhancer(IFileSystem fileSystem)
  24. {
  25. _fileSystem = fileSystem;
  26. }
  27. public bool Supports(IHasImages item, ImageType imageType)
  28. {
  29. return (imageType == ImageType.Primary || imageType == ImageType.Thumb) && item is Playlist;
  30. }
  31. public MetadataProviderPriority Priority
  32. {
  33. get { return MetadataProviderPriority.First; }
  34. }
  35. private List<BaseItem> GetItemsWithImages(IHasImages item)
  36. {
  37. var playlist = (Playlist)item;
  38. var items = playlist.GetManageableItems()
  39. .Select(i =>
  40. {
  41. var subItem = i.Item2;
  42. var episode = subItem as Episode;
  43. if (episode != null)
  44. {
  45. var series = episode.Series;
  46. if (series != null && series.HasImage(ImageType.Primary))
  47. {
  48. return series;
  49. }
  50. }
  51. if (subItem.HasImage(ImageType.Primary))
  52. {
  53. return subItem;
  54. }
  55. var parent = subItem.Parent;
  56. if (parent != null && parent.HasImage(ImageType.Primary))
  57. {
  58. if (parent is MusicAlbum)
  59. {
  60. return parent;
  61. }
  62. }
  63. return null;
  64. })
  65. .Where(i => i != null)
  66. .DistinctBy(i => i.Id)
  67. .ToList();
  68. // Rotate the images no more than once per day
  69. var random = new Random(DateTime.Now.DayOfYear).Next();
  70. return items
  71. .OrderBy(i => random - items.IndexOf(i))
  72. .Take(4)
  73. .OrderBy(i => i.Name)
  74. .ToList();
  75. }
  76. private const string Version = "3";
  77. public string GetConfigurationCacheKey(List<BaseItem> items)
  78. {
  79. return Version + "_" + string.Join(",", items.Select(i => i.Id.ToString("N")).ToArray());
  80. }
  81. public string GetConfigurationCacheKey(IHasImages item, ImageType imageType)
  82. {
  83. var items = GetItemsWithImages(item);
  84. return GetConfigurationCacheKey(items);
  85. }
  86. private const int SquareImageSize = 800;
  87. private const int ThumbImageWidth = 1600;
  88. private const int ThumbImageHeight = 900;
  89. public ImageSize GetEnhancedImageSize(IHasImages item, ImageType imageType, int imageIndex, ImageSize originalImageSize)
  90. {
  91. var items = GetItemsWithImages(item);
  92. if (items.Count == 0)
  93. {
  94. return originalImageSize;
  95. }
  96. if (imageType == ImageType.Thumb)
  97. {
  98. return new ImageSize
  99. {
  100. Height = ThumbImageHeight,
  101. Width = ThumbImageWidth
  102. };
  103. }
  104. return new ImageSize
  105. {
  106. Height = SquareImageSize,
  107. Width = SquareImageSize
  108. };
  109. }
  110. public async Task<Image> EnhanceImageAsync(IHasImages item, Image originalImage, ImageType imageType, int imageIndex)
  111. {
  112. var items = GetItemsWithImages(item);
  113. if (items.Count == 0)
  114. {
  115. return originalImage;
  116. }
  117. var img = imageType == ImageType.Thumb ?
  118. await GetThumbCollage(items).ConfigureAwait(false) :
  119. await GetSquareCollage(items).ConfigureAwait(false);
  120. using (originalImage)
  121. {
  122. return img;
  123. }
  124. }
  125. private Task<Image> GetThumbCollage(List<BaseItem> items)
  126. {
  127. return GetThumbCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList());
  128. }
  129. private Task<Image> GetSquareCollage(List<BaseItem> items)
  130. {
  131. return GetSquareCollage(items.Select(i => i.GetImagePath(ImageType.Primary)).ToList());
  132. }
  133. private async Task<Image> GetThumbCollage(List<string> files)
  134. {
  135. if (files.Count < 3)
  136. {
  137. return await GetSingleImage(files).ConfigureAwait(false);
  138. }
  139. const int rows = 1;
  140. const int cols = 3;
  141. const int cellWidth = 2 * (ThumbImageWidth / 3);
  142. const int cellHeight = ThumbImageHeight;
  143. var index = 0;
  144. var img = new Bitmap(ThumbImageWidth, ThumbImageHeight, PixelFormat.Format32bppPArgb);
  145. using (var graphics = Graphics.FromImage(img))
  146. {
  147. graphics.CompositingQuality = CompositingQuality.HighQuality;
  148. graphics.SmoothingMode = SmoothingMode.HighQuality;
  149. graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  150. graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
  151. graphics.CompositingMode = CompositingMode.SourceCopy;
  152. for (var row = 0; row < rows; row++)
  153. {
  154. for (var col = 0; col < cols; col++)
  155. {
  156. var x = col * (cellWidth / 2);
  157. var y = row * cellHeight;
  158. if (files.Count > index)
  159. {
  160. using (var fileStream = _fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true))
  161. {
  162. using (var memoryStream = new MemoryStream())
  163. {
  164. await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
  165. memoryStream.Position = 0;
  166. using (var imgtemp = Image.FromStream(memoryStream, true, false))
  167. {
  168. graphics.DrawImage(imgtemp, x, y, cellWidth, cellHeight);
  169. }
  170. }
  171. }
  172. }
  173. index++;
  174. }
  175. }
  176. }
  177. return img;
  178. }
  179. private async Task<Image> GetSquareCollage(List<string> files)
  180. {
  181. if (files.Count < 4)
  182. {
  183. return await GetSingleImage(files).ConfigureAwait(false);
  184. }
  185. const int rows = 2;
  186. const int cols = 2;
  187. const int singleSize = SquareImageSize / 2;
  188. var index = 0;
  189. var img = new Bitmap(SquareImageSize, SquareImageSize, PixelFormat.Format32bppPArgb);
  190. using (var graphics = Graphics.FromImage(img))
  191. {
  192. graphics.CompositingQuality = CompositingQuality.HighQuality;
  193. graphics.SmoothingMode = SmoothingMode.HighQuality;
  194. graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  195. graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
  196. graphics.CompositingMode = CompositingMode.SourceCopy;
  197. for (var row = 0; row < rows; row++)
  198. {
  199. for (var col = 0; col < cols; col++)
  200. {
  201. var x = col * singleSize;
  202. var y = row * singleSize;
  203. using (var fileStream = _fileSystem.GetFileStream(files[index], FileMode.Open, FileAccess.Read, FileShare.Read, true))
  204. {
  205. using (var memoryStream = new MemoryStream())
  206. {
  207. await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
  208. memoryStream.Position = 0;
  209. using (var imgtemp = Image.FromStream(memoryStream, true, false))
  210. {
  211. graphics.DrawImage(imgtemp, x, y, singleSize, singleSize);
  212. }
  213. }
  214. }
  215. index++;
  216. }
  217. }
  218. }
  219. return img;
  220. }
  221. private Task<Image> GetSingleImage(List<string> files)
  222. {
  223. return GetImage(files[0]);
  224. }
  225. private async Task<Image> GetImage(string file)
  226. {
  227. using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true))
  228. {
  229. var memoryStream = new MemoryStream();
  230. await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
  231. memoryStream.Position = 0;
  232. return Image.FromStream(memoryStream, true, false);
  233. }
  234. }
  235. }
  236. }