diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-09-18 14:49:06 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-09-18 14:49:06 -0400 |
| commit | d58da2a7728580f79203cfa502269c31c463775d (patch) | |
| tree | 839f627fc09c0198cad153c5dc6c246fc6d1f1b8 | |
| parent | bcd3e8e0faa622fc23025903d3b1d926ccfb2f49 (diff) | |
moved image manager to an interface
17 files changed, 492 insertions, 420 deletions
diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index ebb583506..e68292176 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -13,7 +13,6 @@ using ServiceStack.ServiceHost; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace MediaBrowser.Api.DefaultTheme { @@ -68,24 +67,26 @@ namespace MediaBrowser.Api.DefaultTheme private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; + private readonly IImageProcessor _imageProcessor; - public DefaultThemeService(IUserManager userManager, IDtoService dtoService, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localization) + public DefaultThemeService(IUserManager userManager, IDtoService dtoService, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localization, IImageProcessor imageProcessor) { _userManager = userManager; _dtoService = dtoService; _logger = logger; _libraryManager = libraryManager; _localization = localization; + _imageProcessor = imageProcessor; } public object Get(GetHomeView request) { - var result = GetHomeView(request).Result; + var result = GetHomeView(request); return ToOptimizedResult(result); } - private async Task<HomeView> GetHomeView(GetHomeView request) + private HomeView GetHomeView(GetHomeView request) { var user = _userManager.GetUserById(request.UserId); @@ -113,12 +114,12 @@ namespace MediaBrowser.Api.DefaultTheme public object Get(GetGamesView request) { - var result = GetGamesView(request).Result; + var result = GetGamesView(request); return ToOptimizedResult(result); } - private async Task<GamesView> GetGamesView(GetGamesView request) + private GamesView GetGamesView(GetGamesView request) { var user = _userManager.GetUserById(request.UserId); @@ -145,19 +146,19 @@ namespace MediaBrowser.Api.DefaultTheme public object Get(GetMovieView request) { - var result = GetMovieView(request).Result; + var result = GetMovieView(request); return ToOptimizedResult(result); } public object Get(GetTvView request) { - var result = GetTvView(request).Result; + var result = GetTvView(request); return ToOptimizedResult(result); } - private async Task<TvView> GetTvView(GetTvView request) + private TvView GetTvView(GetTvView request) { var user = _userManager.GetUserById(request.UserId); @@ -210,7 +211,7 @@ namespace MediaBrowser.Api.DefaultTheme return view; } - private async Task<MoviesView> GetMovieView(GetMovieView request) + private MoviesView GetMovieView(GetMovieView request) { var user = _userManager.GetUserById(request.UserId); @@ -348,9 +349,9 @@ namespace MediaBrowser.Api.DefaultTheme { try { - var date = Kernel.Instance.ImageManager.GetImageDateModified(item, path); + var date = item.GetImageDateModified(path); - var size = Kernel.Instance.ImageManager.GetImageSize(path, date); + var size = _imageProcessor.GetImageSize(path, date); return size.Width; } @@ -400,13 +401,11 @@ namespace MediaBrowser.Api.DefaultTheme ImageType = imageType }; - var imageManager = Kernel.Instance.ImageManager; - try { - var imagePath = imageManager.GetImagePath(item, imageType, 0); + var imagePath = item.GetImagePath(imageType, 0); - stub.ImageTag = imageManager.GetImageCacheTag(item, imageType, imagePath); + stub.ImageTag = _imageProcessor.GetImageCacheTag(item, imageType, imagePath); } catch (Exception ex) { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index fb5cb291e..b8c6fc8f0 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -338,11 +339,12 @@ namespace MediaBrowser.Api.Images private readonly IItemRepository _itemRepo; private readonly IDtoService _dtoService; + private readonly IImageProcessor _imageProcessor; /// <summary> /// Initializes a new instance of the <see cref="ImageService" /> class. /// </summary> - public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService) + public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor) { _userManager = userManager; _libraryManager = libraryManager; @@ -350,6 +352,7 @@ namespace MediaBrowser.Api.Images _providerManager = providerManager; _itemRepo = itemRepo; _dtoService = dtoService; + _imageProcessor = imageProcessor; } /// <summary> @@ -403,15 +406,13 @@ namespace MediaBrowser.Api.Images var fileInfo = new FileInfo(path); - var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, path); - - var size = Kernel.Instance.ImageManager.GetImageSize(path, dateModified); + var size = _imageProcessor.GetImageSize(path); list.Add(new ImageInfo { Path = path, ImageType = image.Key, - ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, image.Key, path), + ImageTag = _imageProcessor.GetImageCacheTag(item, image.Key, path), Size = fileInfo.Length, Width = Convert.ToInt32(size.Width), Height = Convert.ToInt32(size.Height) @@ -424,16 +425,14 @@ namespace MediaBrowser.Api.Images { var fileInfo = new FileInfo(image); - var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, image); - - var size = Kernel.Instance.ImageManager.GetImageSize(image, dateModified); + var size = _imageProcessor.GetImageSize(image); list.Add(new ImageInfo { Path = image, ImageIndex = index, ImageType = ImageType.Backdrop, - ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Backdrop, image), + ImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Backdrop, image), Size = fileInfo.Length, Width = Convert.ToInt32(size.Width), Height = Convert.ToInt32(size.Height) @@ -448,16 +447,14 @@ namespace MediaBrowser.Api.Images { var fileInfo = new FileInfo(image); - var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, image); - - var size = Kernel.Instance.ImageManager.GetImageSize(image, dateModified); + var size = _imageProcessor.GetImageSize(image); list.Add(new ImageInfo { Path = image, ImageIndex = index, ImageType = ImageType.Screenshot, - ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Screenshot, image), + ImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Screenshot, image), Size = fileInfo.Length, Width = Convert.ToInt32(size.Width), Height = Convert.ToInt32(size.Height) @@ -480,16 +477,14 @@ namespace MediaBrowser.Api.Images var fileInfo = new FileInfo(image); - var dateModified = Kernel.Instance.ImageManager.GetImageDateModified(item, image); - - var size = Kernel.Instance.ImageManager.GetImageSize(image, dateModified); + var size = _imageProcessor.GetImageSize(image); list.Add(new ImageInfo { Path = image, ImageIndex = index, ImageType = ImageType.Chapter, - ImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Chapter, image), + ImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Chapter, image), Size = fileInfo.Length, Width = Convert.ToInt32(size.Width), Height = Convert.ToInt32(size.Height) @@ -721,11 +716,7 @@ namespace MediaBrowser.Api.Images /// </exception> private object GetImage(ImageRequest request, BaseItem item) { - var kernel = Kernel.Instance; - - var index = request.Index ?? 0; - - var imagePath = GetImagePath(kernel, request, item); + var imagePath = GetImagePath(request, item); if (string.IsNullOrEmpty(imagePath)) { @@ -733,9 +724,9 @@ namespace MediaBrowser.Api.Images } // See if we can avoid a file system lookup by looking for the file in ResolveArgs - var originalFileImageDateModified = kernel.ImageManager.GetImageDateModified(item, imagePath); + var originalFileImageDateModified = item.GetImageDateModified(imagePath); - var supportedImageEnhancers = request.EnableImageEnhancers ? kernel.ImageManager.ImageEnhancers.Where(i => + var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.ImageEnhancers.Where(i => { try { @@ -759,7 +750,7 @@ namespace MediaBrowser.Api.Images var contentType = MimeTypes.GetMimeType(imagePath); - var cacheGuid = kernel.ImageManager.GetImageCacheTag(imagePath, originalFileImageDateModified, supportedImageEnhancers, item, request.Type); + var cacheGuid = _imageProcessor.GetImageCacheTag(item, request.Type, imagePath, originalFileImageDateModified, supportedImageEnhancers); TimeSpan? cacheDuration = null; @@ -778,7 +769,8 @@ namespace MediaBrowser.Api.Images Request = currentRequest, OriginalImageDateModified = originalFileImageDateModified, Enhancers = supportedImageEnhancers, - OriginalImagePath = imagePath + OriginalImagePath = imagePath, + ImageProcessor = _imageProcessor }, contentType); } @@ -786,15 +778,14 @@ namespace MediaBrowser.Api.Images /// <summary> /// Gets the image path. /// </summary> - /// <param name="kernel">The kernel.</param> /// <param name="request">The request.</param> /// <param name="item">The item.</param> /// <returns>System.String.</returns> - private string GetImagePath(Kernel kernel, ImageRequest request, BaseItem item) + private string GetImagePath(ImageRequest request, BaseItem item) { var index = request.Index ?? 0; - return kernel.ImageManager.GetImagePath(item, request.Type, index); + return item.GetImagePath(request.Type, index); } /// <summary> diff --git a/MediaBrowser.Api/Images/ImageWriter.cs b/MediaBrowser.Api/Images/ImageWriter.cs index a734e28e8..da2c9c043 100644 --- a/MediaBrowser.Api/Images/ImageWriter.cs +++ b/MediaBrowser.Api/Images/ImageWriter.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -35,6 +35,8 @@ namespace MediaBrowser.Api.Images public string OriginalImagePath; + public IImageProcessor ImageProcessor { get; set; } + /// <summary> /// The _options /// </summary> @@ -73,7 +75,7 @@ namespace MediaBrowser.Api.Images cropwhitespace = Request.CropWhitespace.Value; } - return Kernel.Instance.ImageManager.ProcessImage(Item, Request.Type, Request.Index ?? 0, OriginalImagePath, cropwhitespace, + return ImageProcessor.ProcessImage(Item, Request.Type, Request.Index ?? 0, OriginalImagePath, cropwhitespace, OriginalImageDateModified, responseStream, Request.Width, Request.Height, Request.MaxWidth, Request.MaxHeight, Request.Quality, Enhancers); } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index d935fea79..915ec0c10 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; @@ -41,16 +42,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// </summary> public class AudioService : BaseProgressiveStreamingService { - /// <summary> - /// Initializes a new instance of the <see cref="AudioService"/> class. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <param name="userManager">The user manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="isoManager">The iso manager.</param> - /// <param name="mediaEncoder">The media encoder.</param> - public AudioService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService) + public AudioService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService, imageProcessor) { } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 806e55024..0bc147a46 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.Net; using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -24,11 +25,13 @@ namespace MediaBrowser.Api.Playback.Progressive public abstract class BaseProgressiveStreamingService : BaseStreamingService { protected readonly IItemRepository ItemRepository; + protected readonly IImageProcessor ImageProcessor; - protected BaseProgressiveStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepository, IDtoService dtoService) : + protected BaseProgressiveStreamingService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepository, IDtoService dtoService, IImageProcessor imageProcessor) : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService) { ItemRepository = itemRepository; + ImageProcessor = imageProcessor; } /// <summary> @@ -303,7 +306,7 @@ namespace MediaBrowser.Api.Playback.Progressive } } - return new ImageService(UserManager, LibraryManager, ApplicationPaths, null, ItemRepository, DtoService) + return new ImageService(UserManager, LibraryManager, ApplicationPaths, null, ItemRepository, DtoService, ImageProcessor) { Logger = Logger, RequestContext = RequestContext, diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index eb53bc2ce..c1dd7fa01 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -53,16 +54,8 @@ namespace MediaBrowser.Api.Playback.Progressive /// </summary> public class VideoService : BaseProgressiveStreamingService { - /// <summary> - /// Initializes a new instance of the <see cref="VideoService"/> class. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <param name="userManager">The user manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="isoManager">The iso manager.</param> - /// <param name="mediaEncoder">The media encoder.</param> - public VideoService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService) - : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService) + public VideoService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor) + : base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, itemRepo, dtoService, imageProcessor) { } diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 2b3f11761..78c299fbb 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -66,6 +67,7 @@ namespace MediaBrowser.Api private readonly ILibrarySearchEngine _searchEngine; private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; + private readonly IImageProcessor _imageProcessor; /// <summary> /// Initializes a new instance of the <see cref="SearchService" /> class. @@ -73,12 +75,13 @@ namespace MediaBrowser.Api /// <param name="userManager">The user manager.</param> /// <param name="searchEngine">The search engine.</param> /// <param name="libraryManager">The library manager.</param> - public SearchService(IUserManager userManager, ILibrarySearchEngine searchEngine, ILibraryManager libraryManager, IDtoService dtoService) + public SearchService(IUserManager userManager, ILibrarySearchEngine searchEngine, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor) { _userManager = userManager; _searchEngine = searchEngine; _libraryManager = libraryManager; _dtoService = dtoService; + _imageProcessor = imageProcessor; } /// <summary> @@ -161,7 +164,7 @@ namespace MediaBrowser.Api if (item.HasImage(ImageType.Primary)) { - result.PrimaryImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Primary, item.GetImage(ImageType.Primary)); + result.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary, item.GetImage(ImageType.Primary)); } var episode = item as Episode; diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs new file mode 100644 index 000000000..55c279b0c --- /dev/null +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -0,0 +1,94 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Drawing +{ + /// <summary> + /// Interface IImageProcessor + /// </summary> + public interface IImageProcessor + { + /// <summary> + /// Gets the image enhancers. + /// </summary> + /// <value>The image enhancers.</value> + IEnumerable<IImageEnhancer> ImageEnhancers { get; } + + /// <summary> + /// Gets the size of the image. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>ImageSize.</returns> + ImageSize GetImageSize(string path); + + /// <summary> + /// Gets the size of the image. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="imageDateModified">The image date modified.</param> + /// <returns>ImageSize.</returns> + ImageSize GetImageSize(string path, DateTime imageDateModified); + + /// <summary> + /// Adds the parts. + /// </summary> + /// <param name="enhancers">The enhancers.</param> + void AddParts(IEnumerable<IImageEnhancer> enhancers); + + /// <summary> + /// Gets the supported enhancers. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="imageType">Type of the image.</param> + /// <returns>IEnumerable{IImageEnhancer}.</returns> + IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType); + + /// <summary> + /// Gets the image cache tag. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="imageType">Type of the image.</param> + /// <param name="imagePath">The image path.</param> + /// <returns>Guid.</returns> + Guid GetImageCacheTag(BaseItem item, ImageType imageType, string imagePath); + + /// <summary> + /// Gets the image cache tag. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="imageType">Type of the image.</param> + /// <param name="originalImagePath">The original image path.</param> + /// <param name="dateModified">The date modified.</param> + /// <param name="imageEnhancers">The image enhancers.</param> + /// <returns>Guid.</returns> + Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, + IEnumerable<IImageEnhancer> imageEnhancers); + + /// <summary> + /// Processes the image. + /// </summary> + /// <param name="entity">The entity.</param> + /// <param name="imageType">Type of the image.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <param name="originalImagePath">The original image path.</param> + /// <param name="cropWhitespace">if set to <c>true</c> [crop whitespace].</param> + /// <param name="dateModified">The date modified.</param> + /// <param name="toStream">To stream.</param> + /// <param name="width">The width.</param> + /// <param name="height">The height.</param> + /// <param name="maxWidth">Width of the max.</param> + /// <param name="maxHeight">Height of the max.</param> + /// <param name="quality">The quality.</param> + /// <param name="enhancers">The enhancers.</param> + /// <returns>Task.</returns> + Task ProcessImage(BaseItem entity, ImageType imageType, int imageIndex, string originalImagePath, bool cropWhitespace, + DateTime dateModified, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, + int? quality, List<IImageEnhancer> enhancers); + } +} diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f81485867..db3e546d1 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; @@ -936,7 +937,7 @@ namespace MediaBrowser.Controller.Entities var itemsChanged = !LocalTrailerIds.SequenceEqual(newItemIds); - var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders)); + var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); @@ -952,7 +953,7 @@ namespace MediaBrowser.Controller.Entities var themeVideosChanged = !ThemeVideoIds.SequenceEqual(newThemeVideoIds); - var tasks = newThemeVideos.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders)); + var tasks = newThemeVideos.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); @@ -971,7 +972,7 @@ namespace MediaBrowser.Controller.Entities var themeSongsChanged = !ThemeSongIds.SequenceEqual(newThemeSongIds); - var tasks = newThemeSongs.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders)); + var tasks = newThemeSongs.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); @@ -1562,5 +1563,64 @@ namespace MediaBrowser.Controller.Entities ScreenshotImagePaths.Remove(path); } } + + /// <summary> + /// Gets the image path. + /// </summary> + /// <param name="imageType">Type of the image.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <returns>System.String.</returns> + /// <exception cref="System.InvalidOperationException"> + /// </exception> + /// <exception cref="System.ArgumentNullException">item</exception> + public string GetImagePath(ImageType imageType, int imageIndex) + { + if (imageType == ImageType.Backdrop) + { + return BackdropImagePaths[imageIndex]; + } + + if (imageType == ImageType.Screenshot) + { + return ScreenshotImagePaths[imageIndex]; + } + + if (imageType == ImageType.Chapter) + { + return ItemRepository.GetChapter(Id, imageIndex).ImagePath; + } + + return GetImage(imageType); + } + + /// <summary> + /// Gets the image date modified. + /// </summary> + /// <param name="imagePath">The image path.</param> + /// <returns>DateTime.</returns> + /// <exception cref="System.ArgumentNullException">item</exception> + public DateTime GetImageDateModified(string imagePath) + { + if (string.IsNullOrEmpty(imagePath)) + { + throw new ArgumentNullException("imagePath"); + } + + var metaFileEntry = ResolveArgs.GetMetaFileByPath(imagePath); + + // If we didn't the metafile entry, check the Season + if (metaFileEntry == null) + { + var episode = this as Episode; + + if (episode != null && episode.Season != null) + { + episode.Season.ResolveArgs.GetMetaFileByPath(imagePath); + } + } + + // See if we can avoid a file system lookup by looking for the file in ResolveArgs + return metaFileEntry == null ? File.GetLastWriteTimeUtc(imagePath) : metaFileEntry.LastWriteTimeUtc; + } } } diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 60e169936..152767860 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Controller.Entities.Movies var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds); - var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders)); + var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 5bf485229..37a1648c1 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.MediaInfo; +using MediaBrowser.Controller.MediaInfo; namespace MediaBrowser.Controller { @@ -15,12 +14,6 @@ namespace MediaBrowser.Controller public static Kernel Instance { get; private set; } /// <summary> - /// Gets the image manager. - /// </summary> - /// <value>The image manager.</value> - public ImageManager ImageManager { get; set; } - - /// <summary> /// Gets the FFMPEG controller. /// </summary> /// <value>The FFMPEG controller.</value> diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 0e97a9f2a..960f597ac 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -71,6 +71,7 @@ <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="Drawing\IImageProcessor.cs" /> <Compile Include="Dto\IDtoService.cs" /> <Compile Include="Entities\AdultVideo.cs" /> <Compile Include="Entities\Audio\IHasAlbumArtist.cs" /> @@ -95,8 +96,6 @@ <Compile Include="Providers\IDynamicInfoProvider.cs" /> <Compile Include="Session\ISessionManager.cs" /> <Compile Include="Drawing\ImageExtensions.cs" /> - <Compile Include="Drawing\ImageHeader.cs" /> - <Compile Include="Drawing\ImageManager.cs" /> <Compile Include="Entities\AggregateFolder.cs" /> <Compile Include="Entities\Audio\Artist.cs" /> <Compile Include="Entities\Audio\Audio.cs" /> diff --git a/MediaBrowser.Controller/Drawing/ImageHeader.cs b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs index 95a753f00..4da836cc6 100644 --- a/MediaBrowser.Controller/Drawing/ImageHeader.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs @@ -5,7 +5,7 @@ using System.Drawing; using System.IO; using System.Linq; -namespace MediaBrowser.Controller.Drawing +namespace MediaBrowser.Server.Implementations.Drawing { /// <summary> /// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349 diff --git a/MediaBrowser.Controller/Drawing/ImageManager.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 05f45a457..d16c2a4de 100644 --- a/MediaBrowser.Controller/Drawing/ImageManager.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -1,108 +1,79 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Globalization; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Drawing; +using System; +using System.Collections.Concurrent; +using System.IO; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; -namespace MediaBrowser.Controller.Drawing +namespace MediaBrowser.Server.Implementations.Drawing { /// <summary> - /// Class ImageManager + /// Class ImageProcessor /// </summary> - public class ImageManager + public class ImageProcessor : IImageProcessor { /// <summary> - /// Gets the list of currently registered image processors - /// Image processors are specialized metadata providers that run after the normal ones - /// </summary> - /// <value>The image enhancers.</value> - public IEnumerable<IImageEnhancer> ImageEnhancers { get; set; } - - /// <summary> - /// Gets the image size cache. + /// The us culture /// </summary> - /// <value>The image size cache.</value> - private FileSystemRepository ImageSizeCache { get; set; } - - /// <summary> - /// Gets or sets the resized image cache. - /// </summary> - /// <value>The resized image cache.</value> - private FileSystemRepository ResizedImageCache { get; set; } - /// <summary> - /// Gets the cropped image cache. - /// </summary> - /// <value>The cropped image cache.</value> - private FileSystemRepository CroppedImageCache { get; set; } + protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); /// <summary> - /// Gets the cropped image cache. + /// The _cached imaged sizes /// </summary> - /// <value>The cropped image cache.</value> - private FileSystemRepository EnhancedImageCache { get; set; } + private readonly ConcurrentDictionary<string, ImageSize> _cachedImagedSizes = new ConcurrentDictionary<string, ImageSize>(); /// <summary> - /// The cached imaged sizes + /// Gets the list of currently registered image processors + /// Image processors are specialized metadata providers that run after the normal ones /// </summary> - private readonly ConcurrentDictionary<string, ImageSize> _cachedImagedSizes = new ConcurrentDictionary<string, ImageSize>(); + /// <value>The image enhancers.</value> + public IEnumerable<IImageEnhancer> ImageEnhancers { get; private set; } /// <summary> /// The _logger /// </summary> private readonly ILogger _logger; - - private readonly IItemRepository _itemRepo; - /// <summary> - /// Initializes a new instance of the <see cref="ImageManager" /> class. + /// The _app paths /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="appPaths">The app paths.</param> - /// <param name="itemRepo">The item repo.</param> - public ImageManager(ILogger logger, IServerApplicationPaths appPaths, IItemRepository itemRepo) + private readonly IServerApplicationPaths _appPaths; + + private readonly string _imageSizeCachePath; + private readonly string _croppedWhitespaceImageCachePath; + private readonly string _enhancedImageCachePath; + private readonly string _resizedImageCachePath; + + public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths) { _logger = logger; - _itemRepo = itemRepo; + _appPaths = appPaths; - ImageSizeCache = new FileSystemRepository(Path.Combine(appPaths.ImageCachePath, "image-sizes")); - ResizedImageCache = new FileSystemRepository(Path.Combine(appPaths.ImageCachePath, "resized-images")); - CroppedImageCache = new FileSystemRepository(Path.Combine(appPaths.ImageCachePath, "cropped-images")); - EnhancedImageCache = new FileSystemRepository(Path.Combine(appPaths.ImageCachePath, "enhanced-images")); + _imageSizeCachePath = Path.Combine(_appPaths.ImageCachePath, "image-sizes"); + _croppedWhitespaceImageCachePath = Path.Combine(_appPaths.ImageCachePath, "cropped-images"); + _enhancedImageCachePath = Path.Combine(_appPaths.ImageCachePath, "enhanced-images"); + _resizedImageCachePath = Path.Combine(_appPaths.ImageCachePath, "resized-images"); + } + + public void AddParts(IEnumerable<IImageEnhancer> enhancers) + { + ImageEnhancers = enhancers.ToArray(); } - /// <summary> - /// Processes an image by resizing to target dimensions - /// </summary> - /// <param name="entity">The entity that owns the image</param> - /// <param name="imageType">The image type</param> - /// <param name="imageIndex">The image index (currently only used with backdrops)</param> - /// <param name="originalImagePath">The original image path.</param> - /// <param name="cropWhitespace">if set to <c>true</c> [crop whitespace].</param> - /// <param name="dateModified">The last date modified of the original image file</param> - /// <param name="toStream">The stream to save the new image to</param> - /// <param name="width">Use if a fixed width is required. Aspect ratio will be preserved.</param> - /// <param name="height">Use if a fixed height is required. Aspect ratio will be preserved.</param> - /// <param name="maxWidth">Use if a max width is required. Aspect ratio will be preserved.</param> - /// <param name="maxHeight">Use if a max height is required. Aspect ratio will be preserved.</param> - /// <param name="quality">Quality level, from 0-100. Currently only applies to JPG. The default value should suffice.</param> - /// <param name="enhancers">The enhancers.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException">entity</exception> public async Task ProcessImage(BaseItem entity, ImageType imageType, int imageIndex, string originalImagePath, bool cropWhitespace, DateTime dateModified, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality, List<IImageEnhancer> enhancers) { if (entity == null) @@ -117,7 +88,7 @@ namespace MediaBrowser.Controller.Drawing if (cropWhitespace) { - originalImagePath = await GetCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); + originalImagePath = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); } // No enhancement - don't cache @@ -247,6 +218,76 @@ namespace MediaBrowser.Controller.Drawing } /// <summary> + /// Crops whitespace from an image, caches the result, and returns the cached path + /// </summary> + /// <param name="originalImagePath">The original image path.</param> + /// <param name="dateModified">The date modified.</param> + /// <returns>System.String.</returns> + private async Task<string> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified) + { + var name = originalImagePath; + name += "datemodified=" + dateModified.Ticks; + + var croppedImagePath = GetCachePath(_croppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath)); + + var semaphore = GetLock(croppedImagePath); + + await semaphore.WaitAsync().ConfigureAwait(false); + + // Check again in case of contention + if (File.Exists(croppedImagePath)) + { + semaphore.Release(); + return croppedImagePath; + } + + try + { + using (var fileStream = new FileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) + { + // Copy to memory stream to avoid Image locking file + using (var memoryStream = new MemoryStream()) + { + await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); + + using (var originalImage = (Bitmap)Image.FromStream(memoryStream, true, false)) + { + var outputFormat = originalImage.RawFormat; + + using (var croppedImage = originalImage.CropWhitespace()) + { + var parentPath = Path.GetDirectoryName(croppedImagePath); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + + using (var outputStream = new FileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + croppedImage.Save(outputFormat, outputStream, 100); + } + } + } + } + } + } + catch (Exception ex) + { + // We have to have a catch-all here because some of the .net image methods throw a plain old Exception + _logger.ErrorException("Error cropping image {0}", ex, originalImagePath); + + return originalImagePath; + } + finally + { + semaphore.Release(); + } + + return croppedImagePath; + } + + /// <summary> /// Caches the resized image. /// </summary> /// <param name="cacheFilePath">The cache file path.</param> @@ -288,31 +329,40 @@ namespace MediaBrowser.Controller.Drawing filename += "datemodified=" + dateModified.Ticks; - return ResizedImageCache.GetResourcePath(filename, Path.GetExtension(originalPath)); + return GetCachePath(_resizedImageCachePath, filename, Path.GetExtension(originalPath)); } + /// <summary> + /// Gets the size of the image. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>ImageSize.</returns> + public ImageSize GetImageSize(string path) + { + return GetImageSize(path, File.GetLastWriteTimeUtc(path)); + } /// <summary> - /// Gets image dimensions + /// Gets the size of the image. /// </summary> - /// <param name="imagePath">The image path.</param> - /// <param name="dateModified">The date modified.</param> - /// <returns>Task{ImageSize}.</returns> - /// <exception cref="System.ArgumentNullException">imagePath</exception> - public ImageSize GetImageSize(string imagePath, DateTime dateModified) + /// <param name="path">The path.</param> + /// <param name="imageDateModified">The image date modified.</param> + /// <returns>ImageSize.</returns> + /// <exception cref="System.ArgumentNullException">path</exception> + public ImageSize GetImageSize(string path, DateTime imageDateModified) { - if (string.IsNullOrEmpty(imagePath)) + if (string.IsNullOrEmpty(path)) { - throw new ArgumentNullException("imagePath"); + throw new ArgumentNullException("path"); } - var name = imagePath + "datemodified=" + dateModified.Ticks; + var name = path + "datemodified=" + imageDateModified.Ticks; ImageSize size; if (!_cachedImagedSizes.TryGetValue(name, out size)) { - size = GetImageSize(name, imagePath); + size = GetImageSizeInternal(name, path); _cachedImagedSizes.AddOrUpdate(name, size, (keyName, oldValue) => size); } @@ -320,18 +370,16 @@ namespace MediaBrowser.Controller.Drawing return size; } - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// <summary> - /// Gets the size of the image. + /// Gets the image size internal. /// </summary> - /// <param name="keyName">Name of the key.</param> - /// <param name="imagePath">The image path.</param> + /// <param name="cacheKey">The cache key.</param> + /// <param name="path">The path.</param> /// <returns>ImageSize.</returns> - private ImageSize GetImageSize(string keyName, string imagePath) + private ImageSize GetImageSizeInternal(string cacheKey, string path) { // Now check the file system cache - var fullCachePath = ImageSizeCache.GetResourcePath(keyName, ".txt"); + var fullCachePath = GetCachePath(_imageSizeCachePath, cacheKey, ".txt"); try { @@ -366,7 +414,7 @@ namespace MediaBrowser.Controller.Drawing // Cache file doesn't exist no biggie } - var size = ImageHeader.GetDimensions(imagePath, _logger); + var size = ImageHeader.GetDimensions(path, _logger); var parentPath = Path.GetDirectoryName(fullCachePath); @@ -383,173 +431,64 @@ namespace MediaBrowser.Controller.Drawing } /// <summary> - /// Gets the image path. + /// Gets the image cache tag. /// </summary> /// <param name="item">The item.</param> /// <param name="imageType">Type of the image.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <returns>System.String.</returns> + /// <param name="imagePath">The image path.</param> + /// <returns>Guid.</returns> /// <exception cref="System.ArgumentNullException">item</exception> - /// <exception cref="System.InvalidOperationException"></exception> - public string GetImagePath(BaseItem item, ImageType imageType, int imageIndex) + public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string imagePath) { if (item == null) { throw new ArgumentNullException("item"); } - if (imageType == ImageType.Backdrop) + if (string.IsNullOrEmpty(imagePath)) { - if (item.BackdropImagePaths == null) - { - throw new InvalidOperationException(string.Format("Item {0} does not have any Backdrops.", item.Name)); - } - - return item.BackdropImagePaths[imageIndex]; + throw new ArgumentNullException("imagePath"); } - if (imageType == ImageType.Screenshot) - { - if (item.ScreenshotImagePaths == null) - { - throw new InvalidOperationException(string.Format("Item {0} does not have any Screenshots.", item.Name)); - } + var dateModified = item.GetImageDateModified(imagePath); - return item.ScreenshotImagePaths[imageIndex]; - } - - if (imageType == ImageType.Chapter) - { - return _itemRepo.GetChapter(item.Id, imageIndex).ImagePath; - } + var supportedEnhancers = GetSupportedEnhancers(item, imageType).ToList(); - return item.GetImage(imageType); + return GetImageCacheTag(item, imageType, imagePath, dateModified, supportedEnhancers); } /// <summary> - /// Gets the image date modified. + /// Gets the image cache tag. /// </summary> /// <param name="item">The item.</param> /// <param name="imageType">Type of the image.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <returns>DateTime.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - public DateTime GetImageDateModified(BaseItem item, ImageType imageType, int imageIndex) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - var imagePath = GetImagePath(item, imageType, imageIndex); - - return GetImageDateModified(item, imagePath); - } - - /// <summary> - /// Gets the image date modified. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="imagePath">The image path.</param> - /// <returns>DateTime.</returns> + /// <param name="originalImagePath">The original image path.</param> + /// <param name="dateModified">The date modified of the original image file.</param> + /// <param name="imageEnhancers">The image enhancers.</param> + /// <returns>Guid.</returns> /// <exception cref="System.ArgumentNullException">item</exception> - public DateTime GetImageDateModified(BaseItem item, string imagePath) + public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, IEnumerable<IImageEnhancer> imageEnhancers) { if (item == null) { throw new ArgumentNullException("item"); } - if (string.IsNullOrEmpty(imagePath)) - { - throw new ArgumentNullException("imagePath"); - } - - var metaFileEntry = item.ResolveArgs.GetMetaFileByPath(imagePath); - - // If we didn't the metafile entry, check the Season - if (metaFileEntry == null) - { - var episode = item as Episode; - - if (episode != null && episode.Season != null) - { - episode.Season.ResolveArgs.GetMetaFileByPath(imagePath); - } - } - - // See if we can avoid a file system lookup by looking for the file in ResolveArgs - return metaFileEntry == null ? File.GetLastWriteTimeUtc(imagePath) : metaFileEntry.LastWriteTimeUtc; - } - - /// <summary> - /// Crops whitespace from an image, caches the result, and returns the cached path - /// </summary> - /// <param name="originalImagePath">The original image path.</param> - /// <param name="dateModified">The date modified.</param> - /// <returns>System.String.</returns> - private async Task<string> GetCroppedImage(string originalImagePath, DateTime dateModified) - { - var name = originalImagePath; - name += "datemodified=" + dateModified.Ticks; - - var croppedImagePath = CroppedImageCache.GetResourcePath(name, Path.GetExtension(originalImagePath)); - - var semaphore = GetLock(croppedImagePath); - - await semaphore.WaitAsync().ConfigureAwait(false); - - // Check again in case of contention - if (File.Exists(croppedImagePath)) + if (imageEnhancers == null) { - semaphore.Release(); - return croppedImagePath; + throw new ArgumentNullException("imageEnhancers"); } - try + if (string.IsNullOrEmpty(originalImagePath)) { - using (var fileStream = new FileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true)) - { - // Copy to memory stream to avoid Image locking file - using (var memoryStream = new MemoryStream()) - { - await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); - - using (var originalImage = (Bitmap)Image.FromStream(memoryStream, true, false)) - { - var outputFormat = originalImage.RawFormat; - - using (var croppedImage = originalImage.CropWhitespace()) - { - var parentPath = Path.GetDirectoryName(croppedImagePath); - - if (!Directory.Exists(parentPath)) - { - Directory.CreateDirectory(parentPath); - } - - using (var outputStream = new FileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - croppedImage.Save(outputFormat, outputStream, 100); - } - } - } - } - } + throw new ArgumentNullException("originalImagePath"); } - catch (Exception ex) - { - // We have to have a catch-all here because some of the .net image methods throw a plain old Exception - _logger.ErrorException("Error cropping image {0}", ex, originalImagePath); - return originalImagePath; - } - finally - { - semaphore.Release(); - } + // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes + var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList(); + cacheKeys.Add(originalImagePath + dateModified.Ticks); - return croppedImagePath; + return string.Join("|", cacheKeys.ToArray()).GetMD5(); } /// <summary> @@ -610,10 +549,10 @@ namespace MediaBrowser.Controller.Drawing throw new ArgumentNullException("item"); } - var cacheGuid = GetImageCacheTag(originalImagePath, dateModified, supportedEnhancers, item, imageType); + var cacheGuid = GetImageCacheTag(item, imageType, originalImagePath, dateModified, supportedEnhancers); // All enhanced images are saved as png to allow transparency - var enhancedImagePath = EnhancedImageCache.GetResourcePath(cacheGuid + ".png"); + var enhancedImagePath = GetCachePath(_enhancedImageCachePath, cacheGuid + ".png"); var semaphore = GetLock(enhancedImagePath); @@ -666,80 +605,6 @@ namespace MediaBrowser.Controller.Drawing } /// <summary> - /// Gets the image cache tag. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="imageType">Type of the image.</param> - /// <param name="imagePath">The image path.</param> - /// <returns>Guid.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string imagePath) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (string.IsNullOrEmpty(imagePath)) - { - throw new ArgumentNullException("imagePath"); - } - - var dateModified = GetImageDateModified(item, imagePath); - - var supportedEnhancers = ImageEnhancers.Where(i => - { - try - { - return i.Supports(item, imageType); - } - catch (Exception ex) - { - _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name); - - return false; - } - - }).ToList(); - - return GetImageCacheTag(imagePath, dateModified, supportedEnhancers, item, imageType); - } - - /// <summary> - /// Gets the image cache tag. - /// </summary> - /// <param name="originalImagePath">The original image path.</param> - /// <param name="dateModified">The date modified of the original image file.</param> - /// <param name="imageEnhancers">The image enhancers.</param> - /// <param name="item">The item.</param> - /// <param name="imageType">Type of the image.</param> - /// <returns>Guid.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - public Guid GetImageCacheTag(string originalImagePath, DateTime dateModified, IEnumerable<IImageEnhancer> imageEnhancers, BaseItem item, ImageType imageType) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (imageEnhancers == null) - { - throw new ArgumentNullException("imageEnhancers"); - } - - if (string.IsNullOrEmpty(originalImagePath)) - { - throw new ArgumentNullException("originalImagePath"); - } - - // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes - var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList(); - cacheKeys.Add(originalImagePath + dateModified.Ticks); - - return string.Join("|", cacheKeys.ToArray()).GetMD5(); - } - - /// <summary> /// Executes the image enhancers. /// </summary> /// <param name="imageEnhancers">The image enhancers.</param> @@ -775,31 +640,113 @@ namespace MediaBrowser.Controller.Drawing /// <summary> /// The _semaphoreLocks /// </summary> - private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>(); + private readonly ConcurrentDictionary<string, object> _locks = new ConcurrentDictionary<string, object>(); /// <summary> /// Gets the lock. /// </summary> /// <param name="filename">The filename.</param> /// <returns>System.Object.</returns> - private SemaphoreSlim GetLock(string filename) + private object GetObjectLock(string filename) { - return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); + return _locks.GetOrAdd(filename, key => new object()); } /// <summary> /// The _semaphoreLocks /// </summary> - private readonly ConcurrentDictionary<string, object> _locks = new ConcurrentDictionary<string, object>(); + private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>(); /// <summary> /// Gets the lock. /// </summary> /// <param name="filename">The filename.</param> /// <returns>System.Object.</returns> - private object GetObjectLock(string filename) + private SemaphoreSlim GetLock(string filename) { - return _locks.GetOrAdd(filename, key => new object()); + return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); + } + + /// <summary> + /// Gets the cache path. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="uniqueName">Name of the unique.</param> + /// <param name="fileExtension">The file extension.</param> + /// <returns>System.String.</returns> + /// <exception cref="System.ArgumentNullException"> + /// path + /// or + /// uniqueName + /// or + /// fileExtension + /// </exception> + public string GetCachePath(string path, string uniqueName, string fileExtension) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException("path"); + } + if (string.IsNullOrEmpty(uniqueName)) + { + throw new ArgumentNullException("uniqueName"); + } + + if (string.IsNullOrEmpty(fileExtension)) + { + throw new ArgumentNullException("fileExtension"); + } + + var filename = uniqueName.GetMD5() + fileExtension; + + return GetCachePath(path, filename); + } + + /// <summary> + /// Gets the cache path. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="filename">The filename.</param> + /// <returns>System.String.</returns> + /// <exception cref="System.ArgumentNullException"> + /// path + /// or + /// filename + /// </exception> + public string GetCachePath(string path, string filename) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException("path"); + } + if (string.IsNullOrEmpty(filename)) + { + throw new ArgumentNullException("filename"); + } + + var prefix = filename.Substring(0, 1); + + path = Path.Combine(path, prefix); + + return Path.Combine(path, filename); + } + + public IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType) + { + return ImageEnhancers.Where(i => + { + try + { + return i.Supports(item as BaseItem, imageType); + } + catch (Exception ex) + { + _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name); + + return false; + } + + }).ToList(); } } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 24b6f0fce..99878e2ec 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1,5 +1,5 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -30,13 +30,16 @@ namespace MediaBrowser.Server.Implementations.Dto private readonly IUserDataRepository _userDataRepository; private readonly IItemRepository _itemRepo; - public DtoService(ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IUserDataRepository userDataRepository, IItemRepository itemRepo) + private readonly IImageProcessor _imageProcessor; + + public DtoService(ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor) { _logger = logger; _libraryManager = libraryManager; _userManager = userManager; _userDataRepository = userDataRepository; _itemRepo = itemRepo; + _imageProcessor = imageProcessor; } /// <summary> @@ -209,7 +212,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (!string.IsNullOrEmpty(image)) { - dto.PrimaryImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(user, ImageType.Primary, image); + dto.PrimaryImageTag = _imageProcessor.GetImageCacheTag(user, ImageType.Primary, image); try { @@ -288,7 +291,7 @@ namespace MediaBrowser.Server.Implementations.Dto { try { - info.PrimaryImageTag = Kernel.Instance.ImageManager.GetImageCacheTag(item, ImageType.Primary, imagePath); + info.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary, imagePath); } catch (IOException) { @@ -409,7 +412,7 @@ namespace MediaBrowser.Server.Implementations.Dto { try { - return Kernel.Instance.ImageManager.GetImageCacheTag(item, type, path); + return _imageProcessor.GetImageCacheTag(item, type, path); } catch (IOException ex) { @@ -1154,7 +1157,7 @@ namespace MediaBrowser.Server.Implementations.Dto try { - size = Kernel.Instance.ImageManager.GetImageSize(path, dateModified); + size = _imageProcessor.GetImageSize(path, dateModified); } catch (FileNotFoundException) { @@ -1169,21 +1172,7 @@ namespace MediaBrowser.Server.Implementations.Dto dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height; - var supportedEnhancers = Kernel.Instance.ImageManager.ImageEnhancers.Where(i => - { - try - { - return i.Supports(item, ImageType.Primary); - } - catch (Exception ex) - { - _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name); - - return false; - } - - }).ToList(); - + var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList(); foreach (var enhancer in supportedEnhancers) { @@ -1199,6 +1188,5 @@ namespace MediaBrowser.Server.Implementations.Dto dto.PrimaryImageAspectRatio = size.Width / size.Height; } - } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 22de1e898..ff9ff4735 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -91,6 +91,7 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath> </Reference> + <Reference Include="System.Drawing" /> <Reference Include="System.Reactive.Core"> <HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll</HintPath> </Reference> @@ -112,6 +113,7 @@ </Compile> <Compile Include="BdInfo\BdInfoExaminer.cs" /> <Compile Include="Configuration\ServerConfigurationManager.cs" /> + <Compile Include="Drawing\ImageHeader.cs" /> <Compile Include="Dto\DtoService.cs" /> <Compile Include="EntryPoints\LibraryChangedNotifier.cs" /> <Compile Include="EntryPoints\LoadRegistrations.cs" /> @@ -128,6 +130,7 @@ <Compile Include="HttpServer\ServerLogger.cs" /> <Compile Include="HttpServer\StreamWriter.cs" /> <Compile Include="HttpServer\SwaggerService.cs" /> + <Compile Include="Drawing\ImageProcessor.cs" /> <Compile Include="IO\DirectoryWatchers.cs" /> <Compile Include="Library\CoreResolutionIgnoreRule.cs" /> <Compile Include="Library\LibraryManager.cs" /> diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index d7e228b18..8f738c5dc 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -34,6 +34,7 @@ using MediaBrowser.Providers; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations.BdInfo; using MediaBrowser.Server.Implementations.Configuration; +using MediaBrowser.Server.Implementations.Drawing; using MediaBrowser.Server.Implementations.Dto; using MediaBrowser.Server.Implementations.HttpServer; using MediaBrowser.Server.Implementations.IO; @@ -160,6 +161,7 @@ namespace MediaBrowser.ServerApplication /// <value>The HTTP server.</value> private IHttpServer HttpServer { get; set; } private IDtoService DtoService { get; set; } + private IImageProcessor ImageProcessor { get; set; } /// <summary> /// Gets or sets the media encoder. @@ -295,7 +297,10 @@ namespace MediaBrowser.ServerApplication LocalizationManager = new LocalizationManager(ServerConfigurationManager); RegisterSingleInstance(LocalizationManager); - DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataRepository, ItemRepository); + ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths); + RegisterSingleInstance(ImageProcessor); + + DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataRepository, ItemRepository, ImageProcessor); RegisterSingleInstance(DtoService); var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false)); @@ -314,11 +319,8 @@ namespace MediaBrowser.ServerApplication /// </summary> private void SetKernelProperties() { - ServerKernel.ImageManager = new ImageManager(LogManager.GetLogger("ImageManager"), - ApplicationPaths, ItemRepository); Parallel.Invoke( () => ServerKernel.FFMpegManager = new FFMpegManager(ApplicationPaths, MediaEncoder, Logger, ItemRepository), - () => ServerKernel.ImageManager.ImageEnhancers = GetExports<IImageEnhancer>().OrderBy(e => e.Priority).ToArray(), () => LocalizedStrings.StringFiles = GetExports<LocalizedStringData>(), SetStaticProperties ); @@ -461,6 +463,8 @@ namespace MediaBrowser.ServerApplication ProviderManager.AddParts(GetExports<BaseMetadataProvider>().ToArray()); IsoManager.AddParts(GetExports<IIsoMounter>().ToArray()); + + ImageProcessor.AddParts(GetExports<IImageEnhancer>().ToArray()); } /// <summary> |
