From d58da2a7728580f79203cfa502269c31c463775d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 18 Sep 2013 14:49:06 -0400 Subject: [PATCH] moved image manager to an interface --- .../DefaultTheme/DefaultThemeService.cs | 33 +- MediaBrowser.Api/Images/ImageService.cs | 49 +- MediaBrowser.Api/Images/ImageWriter.cs | 6 +- .../Playback/Progressive/AudioService.cs | 13 +- .../BaseProgressiveStreamingService.cs | 7 +- .../Playback/Progressive/VideoService.cs | 13 +- MediaBrowser.Api/SearchService.cs | 7 +- .../Drawing/IImageProcessor.cs | 94 +++ MediaBrowser.Controller/Entities/BaseItem.cs | 66 ++- .../Entities/Movies/Movie.cs | 2 +- MediaBrowser.Controller/Kernel.cs | 9 +- .../MediaBrowser.Controller.csproj | 3 +- .../Drawing/ImageHeader.cs | 2 +- .../Drawing/ImageProcessor.cs | 561 ++++++++---------- .../Dto/DtoService.cs | 32 +- ...MediaBrowser.Server.Implementations.csproj | 3 + .../ApplicationHost.cs | 12 +- 17 files changed, 492 insertions(+), 420 deletions(-) create mode 100644 MediaBrowser.Controller/Drawing/IImageProcessor.cs rename {MediaBrowser.Controller => MediaBrowser.Server.Implementations}/Drawing/ImageHeader.cs (99%) rename MediaBrowser.Controller/Drawing/ImageManager.cs => MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs (73%) diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index ebb583506c..e682921765 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 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 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 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 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 fb5cb291ef..b8c6fc8f0b 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; /// /// Initializes a new instance of the class. /// - 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; } /// @@ -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 /// 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 /// /// Gets the image path. /// - /// The kernel. /// The request. /// The item. /// System.String. - 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); } /// diff --git a/MediaBrowser.Api/Images/ImageWriter.cs b/MediaBrowser.Api/Images/ImageWriter.cs index a734e28e80..da2c9c043a 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; } + /// /// The _options /// @@ -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 d935fea79b..915ec0c106 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 /// public class AudioService : BaseProgressiveStreamingService { - /// - /// Initializes a new instance of the class. - /// - /// The app paths. - /// The user manager. - /// The library manager. - /// The iso manager. - /// The media encoder. - 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 806e55024f..0bc147a46c 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; } /// @@ -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 eb53bc2cee..c1dd7fa017 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 /// public class VideoService : BaseProgressiveStreamingService { - /// - /// Initializes a new instance of the class. - /// - /// The app paths. - /// The user manager. - /// The library manager. - /// The iso manager. - /// The media encoder. - 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 2b3f117617..78c299fbb2 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; /// /// Initializes a new instance of the class. @@ -73,12 +75,13 @@ namespace MediaBrowser.Api /// The user manager. /// The search engine. /// The library manager. - 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; } /// @@ -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 0000000000..55c279b0cc --- /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 +{ + /// + /// Interface IImageProcessor + /// + public interface IImageProcessor + { + /// + /// Gets the image enhancers. + /// + /// The image enhancers. + IEnumerable ImageEnhancers { get; } + + /// + /// Gets the size of the image. + /// + /// The path. + /// ImageSize. + ImageSize GetImageSize(string path); + + /// + /// Gets the size of the image. + /// + /// The path. + /// The image date modified. + /// ImageSize. + ImageSize GetImageSize(string path, DateTime imageDateModified); + + /// + /// Adds the parts. + /// + /// The enhancers. + void AddParts(IEnumerable enhancers); + + /// + /// Gets the supported enhancers. + /// + /// The item. + /// Type of the image. + /// IEnumerable{IImageEnhancer}. + IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType); + + /// + /// Gets the image cache tag. + /// + /// The item. + /// Type of the image. + /// The image path. + /// Guid. + Guid GetImageCacheTag(BaseItem item, ImageType imageType, string imagePath); + + /// + /// Gets the image cache tag. + /// + /// The item. + /// Type of the image. + /// The original image path. + /// The date modified. + /// The image enhancers. + /// Guid. + Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, + IEnumerable imageEnhancers); + + /// + /// Processes the image. + /// + /// The entity. + /// Type of the image. + /// Index of the image. + /// The original image path. + /// if set to true [crop whitespace]. + /// The date modified. + /// To stream. + /// The width. + /// The height. + /// Width of the max. + /// Height of the max. + /// The quality. + /// The enhancers. + /// Task. + 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 enhancers); + } +} diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f81485867b..db3e546d1e 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); } } + + /// + /// Gets the image path. + /// + /// Type of the image. + /// Index of the image. + /// System.String. + /// + /// + /// item + 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); + } + + /// + /// Gets the image date modified. + /// + /// The image path. + /// DateTime. + /// item + 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 60e1699363..152767860d 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 5bf485229b..37a1648c17 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 { @@ -14,12 +13,6 @@ namespace MediaBrowser.Controller /// The instance. public static Kernel Instance { get; private set; } - /// - /// Gets the image manager. - /// - /// The image manager. - public ImageManager ImageManager { get; set; } - /// /// Gets the FFMPEG controller. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 0e97a9f2a4..960f597ac3 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -71,6 +71,7 @@ Properties\SharedVersion.cs + @@ -95,8 +96,6 @@ - - diff --git a/MediaBrowser.Controller/Drawing/ImageHeader.cs b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs similarity index 99% rename from MediaBrowser.Controller/Drawing/ImageHeader.cs rename to MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs index 95a753f004..4da836cc68 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 { /// /// 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 similarity index 73% rename from MediaBrowser.Controller/Drawing/ImageManager.cs rename to MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 05f45a4574..d16c2a4ded 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 { /// - /// Class ImageManager + /// Class ImageProcessor /// - public class ImageManager + public class ImageProcessor : IImageProcessor { /// - /// Gets the list of currently registered image processors - /// Image processors are specialized metadata providers that run after the normal ones - /// - /// The image enhancers. - public IEnumerable ImageEnhancers { get; set; } - - /// - /// Gets the image size cache. + /// The us culture /// - /// The image size cache. - private FileSystemRepository ImageSizeCache { get; set; } - - /// - /// Gets or sets the resized image cache. - /// - /// The resized image cache. - private FileSystemRepository ResizedImageCache { get; set; } - /// - /// Gets the cropped image cache. - /// - /// The cropped image cache. - private FileSystemRepository CroppedImageCache { get; set; } + protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); /// - /// Gets the cropped image cache. + /// The _cached imaged sizes /// - /// The cropped image cache. - private FileSystemRepository EnhancedImageCache { get; set; } + private readonly ConcurrentDictionary _cachedImagedSizes = new ConcurrentDictionary(); /// - /// The cached imaged sizes + /// Gets the list of currently registered image processors + /// Image processors are specialized metadata providers that run after the normal ones /// - private readonly ConcurrentDictionary _cachedImagedSizes = new ConcurrentDictionary(); + /// The image enhancers. + public IEnumerable ImageEnhancers { get; private set; } /// /// The _logger /// private readonly ILogger _logger; - - private readonly IItemRepository _itemRepo; - /// - /// Initializes a new instance of the class. + /// The _app paths /// - /// The logger. - /// The app paths. - /// The item repo. - 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 enhancers) + { + ImageEnhancers = enhancers.ToArray(); } - /// - /// Processes an image by resizing to target dimensions - /// - /// The entity that owns the image - /// The image type - /// The image index (currently only used with backdrops) - /// The original image path. - /// if set to true [crop whitespace]. - /// The last date modified of the original image file - /// The stream to save the new image to - /// Use if a fixed width is required. Aspect ratio will be preserved. - /// Use if a fixed height is required. Aspect ratio will be preserved. - /// Use if a max width is required. Aspect ratio will be preserved. - /// Use if a max height is required. Aspect ratio will be preserved. - /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. - /// The enhancers. - /// Task. - /// entity 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 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 @@ -246,6 +217,76 @@ namespace MediaBrowser.Controller.Drawing } } + /// + /// Crops whitespace from an image, caches the result, and returns the cached path + /// + /// The original image path. + /// The date modified. + /// System.String. + private async Task 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; + } + /// /// Caches the resized image. /// @@ -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)); } + /// + /// Gets the size of the image. + /// + /// The path. + /// ImageSize. + public ImageSize GetImageSize(string path) + { + return GetImageSize(path, File.GetLastWriteTimeUtc(path)); + } /// - /// Gets image dimensions + /// Gets the size of the image. /// - /// The image path. - /// The date modified. - /// Task{ImageSize}. - /// imagePath - public ImageSize GetImageSize(string imagePath, DateTime dateModified) + /// The path. + /// The image date modified. + /// ImageSize. + /// path + 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"); - /// - /// Gets the size of the image. + /// Gets the image size internal. /// - /// Name of the key. - /// The image path. + /// The cache key. + /// The path. /// ImageSize. - 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 } /// - /// Gets the image path. + /// Gets the image cache tag. /// /// The item. /// Type of the image. - /// Index of the image. - /// System.String. + /// The image path. + /// Guid. /// item - /// - 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); } /// - /// Gets the image date modified. + /// Gets the image cache tag. /// /// The item. /// Type of the image. - /// Index of the image. - /// DateTime. - /// item - 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); - } - - /// - /// Gets the image date modified. - /// - /// The item. - /// The image path. - /// DateTime. + /// The original image path. + /// The date modified of the original image file. + /// The image enhancers. + /// Guid. /// item - public DateTime GetImageDateModified(BaseItem item, string imagePath) + public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, IEnumerable 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; - } - - /// - /// Crops whitespace from an image, caches the result, and returns the cached path - /// - /// The original image path. - /// The date modified. - /// System.String. - private async Task 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(); } /// @@ -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); @@ -665,80 +604,6 @@ namespace MediaBrowser.Controller.Drawing return enhancedImagePath; } - /// - /// Gets the image cache tag. - /// - /// The item. - /// Type of the image. - /// The image path. - /// Guid. - /// item - 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); - } - - /// - /// Gets the image cache tag. - /// - /// The original image path. - /// The date modified of the original image file. - /// The image enhancers. - /// The item. - /// Type of the image. - /// Guid. - /// item - public Guid GetImageCacheTag(string originalImagePath, DateTime dateModified, IEnumerable 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(); - } - /// /// Executes the image enhancers. /// @@ -775,31 +640,113 @@ namespace MediaBrowser.Controller.Drawing /// /// The _semaphoreLocks /// - private readonly ConcurrentDictionary _semaphoreLocks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); /// /// Gets the lock. /// /// The filename. /// System.Object. - 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()); } /// /// The _semaphoreLocks /// - private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _semaphoreLocks = new ConcurrentDictionary(); /// /// Gets the lock. /// /// The filename. /// System.Object. - 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)); + } + + /// + /// Gets the cache path. + /// + /// The path. + /// Name of the unique. + /// The file extension. + /// System.String. + /// + /// path + /// or + /// uniqueName + /// or + /// fileExtension + /// + 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); + } + + /// + /// Gets the cache path. + /// + /// The path. + /// The filename. + /// System.String. + /// + /// path + /// or + /// filename + /// + 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 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 24b6f0fcee..99878e2ec4 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; } /// @@ -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 22de1e8985..ff9ff4735c 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -91,6 +91,7 @@ False ..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll + ..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll @@ -112,6 +113,7 @@ + @@ -128,6 +130,7 @@ + diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index d7e228b183..8f738c5dcd 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 /// The HTTP server. private IHttpServer HttpServer { get; set; } private IDtoService DtoService { get; set; } + private IImageProcessor ImageProcessor { get; set; } /// /// 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 /// 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().OrderBy(e => e.Priority).ToArray(), () => LocalizedStrings.StringFiles = GetExports(), SetStaticProperties ); @@ -461,6 +463,8 @@ namespace MediaBrowser.ServerApplication ProviderManager.AddParts(GetExports().ToArray()); IsoManager.AddParts(GetExports().ToArray()); + + ImageProcessor.AddParts(GetExports().ToArray()); } ///