From 5e10e0ff192de703e8fa18462fa08aa16abac8c4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 19 Feb 2015 20:57:10 -0500 Subject: replace System.Drawing with ImageMagick --- .../Drawing/ImageProcessor.cs | 304 +++++---------------- 1 file changed, 73 insertions(+), 231 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 5055d2750..e942b183b 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -1,10 +1,9 @@ -using Imazen.WebP; +using ImageMagickSharp; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -13,9 +12,6 @@ using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Linq; @@ -54,14 +50,12 @@ namespace MediaBrowser.Server.Implementations.Drawing private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly IServerApplicationPaths _appPaths; - private readonly IMediaEncoder _mediaEncoder; - public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) + public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer) { _logger = logger; _fileSystem = fileSystem; _jsonSerializer = jsonSerializer; - _mediaEncoder = mediaEncoder; _appPaths = appPaths; _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite); @@ -92,7 +86,7 @@ namespace MediaBrowser.Server.Implementations.Drawing _cachedImagedSizes = new ConcurrentDictionary(sizeDictionary); - LogWebPVersion(); + LogImageMagickVersionVersion(); } private string ResizedImageCachePath @@ -134,13 +128,9 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - public Model.Drawing.ImageFormat[] GetSupportedImageOutputFormats() + public ImageFormat[] GetSupportedImageOutputFormats() { - if (_webpAvailable) - { - return new[] { Model.Drawing.ImageFormat.Webp, Model.Drawing.ImageFormat.Gif, Model.Drawing.ImageFormat.Jpg, Model.Drawing.ImageFormat.Png }; - } - return new[] { Model.Drawing.ImageFormat.Gif, Model.Drawing.ImageFormat.Jpg, Model.Drawing.ImageFormat.Png }; + return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png }; } public async Task ProcessImage(ImageProcessingOptions options) @@ -212,77 +202,42 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0; + var newWidth = Convert.ToInt32(newSize.Width); + var newHeight = Convert.ToInt32(newSize.Height); - using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) + Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); + + if (string.IsNullOrWhiteSpace(options.BackgroundColor)) { - // Copy to memory stream to avoid Image locking file - using (var memoryStream = new MemoryStream()) + using (var originalImage = new MagickWand(originalImagePath)) { - await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); + originalImage.CurrentImage.ResizeImage(newWidth, newHeight); + + DrawIndicator(originalImage, newWidth, newHeight, options); - using (var originalImage = Image.FromStream(memoryStream, true, false)) + originalImage.CurrentImage.CompressionQuality = quality; + + originalImage.SaveImage(cacheFilePath); + + return cacheFilePath; + } + } + else + { + using (var wand = new MagickWand(newWidth, newHeight, options.BackgroundColor)) + { + using (var originalImage = new MagickWand(originalImagePath)) { - var newWidth = Convert.ToInt32(newSize.Width); - var newHeight = Convert.ToInt32(newSize.Height); - - var selectedOutputFormat = options.OutputFormat; - - _logger.Debug("Processing image to {0}", selectedOutputFormat); - - // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here - // Also, Webp only supports Format32bppArgb and Format32bppRgb - var pixelFormat = selectedOutputFormat == Model.Drawing.ImageFormat.Webp - ? PixelFormat.Format32bppArgb - : PixelFormat.Format32bppPArgb; - - using (var thumbnail = new Bitmap(newWidth, newHeight, pixelFormat)) - { - // Mono throw an exeception if assign 0 to SetResolution - if (originalImage.HorizontalResolution > 0 && originalImage.VerticalResolution > 0) - { - // Preserve the original resolution - thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution); - } - - using (var thumbnailGraph = Graphics.FromImage(thumbnail)) - { - thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality; - thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; - thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; - thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; - thumbnailGraph.CompositingMode = !hasPostProcessing ? - CompositingMode.SourceCopy : - CompositingMode.SourceOver; - - SetBackgroundColor(thumbnailGraph, options); - - thumbnailGraph.DrawImage(originalImage, 0, 0, newWidth, newHeight); - - DrawIndicator(thumbnailGraph, newWidth, newHeight, options); - - var outputFormat = GetOutputFormat(originalImage, selectedOutputFormat); - - Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath)); - - // Save to the cache location - using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - if (selectedOutputFormat == Model.Drawing.ImageFormat.Webp) - { - SaveToWebP(thumbnail, cacheFileStream, quality); - } - else - { - // Save to the memory stream - thumbnail.Save(outputFormat, cacheFileStream, quality); - } - } - - return cacheFilePath; - } - } + originalImage.CurrentImage.ResizeImage(newWidth, newHeight); + + wand.CurrentImage.CompositeImage(originalImage, CompositeOperator.OverCompositeOp, 0, 0); + DrawIndicator(wand, newWidth, newHeight, options); + + wand.CurrentImage.CompressionQuality = quality; + wand.SaveImage(cacheFilePath); + + return cacheFilePath; } } } @@ -293,59 +248,26 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - private void SaveToWebP(Bitmap thumbnail, Stream toStream, int quality) - { - new SimpleEncoder().Encode(thumbnail, toStream, quality); - } - - private bool _webpAvailable = true; - private void LogWebPVersion() + private void LogImageMagickVersionVersion() { try { - _logger.Info("libwebp version: " + SimpleEncoder.GetEncoderVersion()); + _logger.Info("ImageMagick version: " + Wand.VersionString); } catch (Exception ex) { - _logger.ErrorException("Error loading libwebp: ", ex); - _webpAvailable = false; - } - } - - /// - /// Sets the color of the background. - /// - /// The graphics. - /// The options. - private void SetBackgroundColor(Graphics graphics, ImageProcessingOptions options) - { - var color = options.BackgroundColor; - - if (!string.IsNullOrEmpty(color)) - { - Color drawingColor; - - try - { - drawingColor = ColorTranslator.FromHtml(color); - } - catch - { - drawingColor = ColorTranslator.FromHtml("#" + color); - } - - graphics.Clear(drawingColor); + _logger.ErrorException("Error loading ImageMagick: ", ex); } } /// /// Draws the indicator. /// - /// The graphics. + /// The wand. /// Width of the image. /// Height of the image. /// The options. - private void DrawIndicator(Graphics graphics, int imageWidth, int imageHeight, ImageProcessingOptions options) + private void DrawIndicator(MagickWand wand, int imageWidth, int imageHeight, ImageProcessingOptions options) { if (!options.AddPlayedIndicator && !options.UnplayedCount.HasValue && options.PercentPlayed.Equals(0)) { @@ -356,22 +278,20 @@ namespace MediaBrowser.Server.Implementations.Drawing { if (options.AddPlayedIndicator) { - var currentImageSize = new Size(imageWidth, imageHeight); + var currentImageSize = new ImageSize(imageWidth, imageHeight); - new PlayedIndicatorDrawer().DrawPlayedIndicator(graphics, currentImageSize); + new PlayedIndicatorDrawer().DrawPlayedIndicator(wand, currentImageSize); } else if (options.UnplayedCount.HasValue) { - var currentImageSize = new Size(imageWidth, imageHeight); + var currentImageSize = new ImageSize(imageWidth, imageHeight); - new UnplayedCountIndicator().DrawUnplayedCountIndicator(graphics, currentImageSize, options.UnplayedCount.Value); + new UnplayedCountIndicator().DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value); } if (options.PercentPlayed > 0) { - var currentImageSize = new Size(imageWidth, imageHeight); - - new PercentPlayedDrawer().Process(graphics, currentImageSize, options.PercentPlayed); + new PercentPlayedDrawer().Process(wand, options.PercentPlayed); } } catch (Exception ex) @@ -380,29 +300,6 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - /// - /// Gets the output format. - /// - /// The image. - /// The output format. - /// ImageFormat. - private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, Model.Drawing.ImageFormat outputFormat) - { - switch (outputFormat) - { - case Model.Drawing.ImageFormat.Bmp: - return System.Drawing.Imaging.ImageFormat.Bmp; - case Model.Drawing.ImageFormat.Gif: - return System.Drawing.Imaging.ImageFormat.Gif; - case Model.Drawing.ImageFormat.Jpg: - return System.Drawing.Imaging.ImageFormat.Jpeg; - case Model.Drawing.ImageFormat.Png: - return System.Drawing.Imaging.ImageFormat.Png; - default: - return image.RawFormat; - } - } - /// /// Crops whitespace from an image, caches the result, and returns the cached path /// @@ -429,28 +326,12 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) + Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); + + using (var wand = new MagickWand(originalImagePath)) { - // 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()) - { - Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); - - using (var outputStream = _fileSystem.GetFileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - croppedImage.Save(outputFormat, outputStream, 100); - } - } - } - } + wand.CurrentImage.TrimImage(10); + wand.SaveImage(croppedImagePath); } } catch (Exception ex) @@ -476,7 +357,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// /// Gets the cache file path based on a set of parameters /// - private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, Model.Drawing.ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) + private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor) { var filename = originalPath; @@ -727,7 +608,7 @@ namespace MediaBrowser.Server.Implementations.Drawing } /// - /// Runs an image through the image enhancers, caches the result, and returns the cached path + /// Gets the enhanced image internal. /// /// The original image path. /// The item. @@ -735,8 +616,12 @@ namespace MediaBrowser.Server.Implementations.Drawing /// Index of the image. /// The supported enhancers. /// The cache unique identifier. - /// System.String. - /// originalImagePath + /// Task<System.String>. + /// + /// originalImagePath + /// or + /// item + /// private async Task GetEnhancedImageInternal(string originalImagePath, IHasImages item, ImageType imageType, @@ -770,51 +655,8 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) - { - // Copy to memory stream to avoid Image locking file - using (var memoryStream = new MemoryStream()) - { - await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false); - - memoryStream.Position = 0; - - var imageStream = new ImageStream - { - Stream = memoryStream, - Format = GetFormat(originalImagePath) - }; - - //Pass the image through registered enhancers - using (var newImageStream = await ExecuteImageEnhancers(supportedEnhancers, imageStream, item, imageType, imageIndex).ConfigureAwait(false)) - { - var parentDirectory = Path.GetDirectoryName(enhancedImagePath); - - Directory.CreateDirectory(parentDirectory); - - // Save as png - if (newImageStream.Format == Model.Drawing.ImageFormat.Png) - { - //And then save it in the cache - using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - await newImageStream.Stream.CopyToAsync(outputStream).ConfigureAwait(false); - } - } - else - { - using (var newImage = Image.FromStream(newImageStream.Stream, true, false)) - { - //And then save it in the cache - using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) - { - newImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100); - } - } - } - } - } - } + Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath)); + await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false); } finally { @@ -824,43 +666,42 @@ namespace MediaBrowser.Server.Implementations.Drawing return enhancedImagePath; } - private Model.Drawing.ImageFormat GetFormat(string path) + private ImageFormat GetFormat(string path) { var extension = Path.GetExtension(path); if (string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Png; + return ImageFormat.Png; } if (string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Gif; + return ImageFormat.Gif; } if (string.Equals(extension, ".webp", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Webp; + return ImageFormat.Webp; } if (string.Equals(extension, ".bmp", StringComparison.OrdinalIgnoreCase)) { - return Model.Drawing.ImageFormat.Bmp; + return ImageFormat.Bmp; } - return Model.Drawing.ImageFormat.Jpg; + return ImageFormat.Jpg; } /// /// Executes the image enhancers. /// /// The image enhancers. - /// The original image. + /// The input path. + /// The output path. /// The item. /// Type of the image. /// Index of the image. /// Task{EnhancedImage}. - private async Task ExecuteImageEnhancers(IEnumerable imageEnhancers, ImageStream originalImage, IHasImages item, ImageType imageType, int imageIndex) + private async Task ExecuteImageEnhancers(IEnumerable imageEnhancers, string inputPath, string outputPath, IHasImages item, ImageType imageType, int imageIndex) { - var result = originalImage; - // Run the enhancers sequentially in order of priority foreach (var enhancer in imageEnhancers) { @@ -868,7 +709,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { - result = await enhancer.EnhanceImageAsync(item, result, imageType, imageIndex).ConfigureAwait(false); + await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false); } catch (Exception ex) { @@ -876,9 +717,10 @@ namespace MediaBrowser.Server.Implementations.Drawing throw; } - } - return result; + // Feed the output into the next enhancer as input + inputPath = outputPath; + } } /// -- cgit v1.2.3 From a0c6c259e691e42905a80112993cfd419c82138d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 22 Feb 2015 00:45:29 -0500 Subject: dispose image magick environment on shutdown --- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 2 -- MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs | 1 + MediaBrowser.Server.Implementations/Library/UserViewManager.cs | 3 +-- .../Localization/JavaScript/javascript.json | 2 +- MediaBrowser.ServerApplication/MainStartup.cs | 5 +++-- 5 files changed, 6 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index a28d3bd5d..bb57e9d47 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -203,8 +203,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableAudioArchiveFiles { get; set; } public bool EnableVideoArchiveFiles { get; set; } - public bool EnableLegacyCollectionInView { get; set; } - /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index e942b183b..85eadd73c 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -822,6 +822,7 @@ namespace MediaBrowser.Server.Implementations.Drawing public void Dispose() { + Wand.CloseEnvironment(); _saveImageSizeTimer.Dispose(); } } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 71a360900..8b7cfa9f2 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -85,8 +85,7 @@ namespace MediaBrowser.Server.Implementations.Library list.Add(await GetUserView(CollectionType.Movies, string.Empty, cancellationToken).ConfigureAwait(false)); } - if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase)) - || _config.Configuration.EnableLegacyCollectionInView) + if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))) { list.Add(await GetUserView(CollectionType.Games, string.Empty, cancellationToken).ConfigureAwait(false)); } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 09045be9b..691e17d78 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -69,7 +69,7 @@ "ButtonAddToCollection": "Add to collection", "HeaderSelectCertificatePath": "Select Certificate Path", "ConfirmMessageScheduledTaskButton": "This operation normally runs automatically as a scheduled task. It can also be run manually here. To configure the scheduled task, see:", - "HeaderSupporterBenefit": "A supporter membership provides additional benefits such as access to premium plugins, internet channel content, and more. {0}Learn more{1}.", + "HeaderSupporterBenefit": "A supporter membership provides additional benefits such as access to sync, premium plugins, internet channel content, and more. {0}Learn more{1}.", "LabelSyncNoTargetsHelp": "It looks like you don't currently have any apps that support sync.", "HeaderWelcomeToMediaBrowserServerDashboard": "Welcome to the Media Browser Dashboard", "HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client", diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index bc3bef1a0..4bf51bc6b 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Implementations.Logging; +using ImageMagickSharp; +using MediaBrowser.Common.Implementations.Logging; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Startup.Common; @@ -40,7 +41,7 @@ namespace MediaBrowser.ServerApplication var applicationPath = currentProcess.MainModule.FileName; - ImageMagickSharp.Wand.SetMagickCoderModulePath(Path.Combine(Path.GetDirectoryName(applicationPath), "ImageMagickCoders", "x86")); + Wand.SetMagickCoderModulePath(Path.Combine(Path.GetDirectoryName(applicationPath), "ImageMagickCoders", "x86")); var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService); var logManager = new NlogManager(appPaths.LogDirectoryPath, "server"); -- cgit v1.2.3 From 2bf2d5fd769788cade10a84fc7a4a4af23c23cf1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 28 Feb 2015 08:42:47 -0500 Subject: cloud sync updates --- MediaBrowser.Api/Images/ImageService.cs | 2 +- MediaBrowser.Api/Playback/TranscodingThrottler.cs | 50 ++--- MediaBrowser.Controller/Drawing/IImageProcessor.cs | 6 +- .../MediaBrowser.Controller.csproj | 2 +- MediaBrowser.Controller/Sync/ICloudSyncProvider.cs | 46 ---- .../Sync/IServerSyncProvider.cs | 54 ++++- MediaBrowser.Controller/Sync/ISyncDataProvider.cs | 41 ++++ MediaBrowser.Controller/Sync/ISyncProvider.cs | 7 - MediaBrowser.Dlna/Didl/DidlBuilder.cs | 2 +- MediaBrowser.Model/Users/UserPolicy.cs | 2 + MediaBrowser.Providers/Photos/PhotoProvider.cs | 2 +- .../Drawing/ImageProcessor.cs | 7 +- .../Dto/DtoService.cs | 5 +- .../Localization/Server/server.json | 6 +- .../MediaBrowser.Server.Implementations.csproj | 6 +- .../Sync/AppSyncProvider.cs | 2 +- .../Sync/CloudSyncProfile.cs | 118 ++++++++++ .../Sync/CloudSyncProvider.cs | 57 ----- .../Sync/FolderSync/FolderSyncDataProvider.cs | 31 +++ .../Sync/FolderSync/FolderSyncProvider.cs | 142 ++++++++++++ .../Sync/IHasSyncProfile.cs | 15 ++ .../Sync/MediaSync.cs | 249 ++++++++++++++++++--- .../Sync/MultiProviderSync.cs | 69 ++++++ .../Sync/SyncManager.cs | 21 +- 24 files changed, 732 insertions(+), 210 deletions(-) delete mode 100644 MediaBrowser.Controller/Sync/ICloudSyncProvider.cs create mode 100644 MediaBrowser.Controller/Sync/ISyncDataProvider.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs delete mode 100644 MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index e29bbf674..0a39b51c3 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -318,7 +318,7 @@ namespace MediaBrowser.Api.Images try { - var size = _imageProcessor.GetImageSize(info.Path, info.DateModified); + var size = _imageProcessor.GetImageSize(info); width = Convert.ToInt32(size.Width); height = Convert.ToInt32(size.Height); diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index 81848c017..50c213655 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -13,11 +13,17 @@ namespace MediaBrowser.Api.Playback public void Start() { - _timer = new Timer(TimerCallback, null, 1000, 1000); + _timer = new Timer(TimerCallback, null, 5000, 5000); } private void TimerCallback(object state) { + if (_job.HasExited) + { + DisposeTimer(); + return; + } + if (IsThrottleAllowed(_job)) { PauseTranscoding(); @@ -50,36 +56,6 @@ namespace MediaBrowser.Api.Playback private bool IsThrottleAllowed(TranscodingJob job) { - //var job = string.IsNullOrEmpty(request.TranscodingJobId) ? - //null : - //ApiEntryPoint.Instance.GetTranscodingJob(request.TranscodingJobId); - - //var limits = new List(); - //if (state.InputBitrate.HasValue) - //{ - // // Bytes per second - // limits.Add((state.InputBitrate.Value / 8)); - //} - //if (state.InputFileSize.HasValue && state.RunTimeTicks.HasValue) - //{ - // var totalSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds; - - // if (totalSeconds > 1) - // { - // var timeBasedLimit = state.InputFileSize.Value / totalSeconds; - // limits.Add(Convert.ToInt64(timeBasedLimit)); - // } - //} - - //// Take the greater of the above to methods, just to be safe - //var throttleLimit = limits.Count > 0 ? limits.First() : 0; - - //// Pad to play it safe - //var bytesPerSecond = Convert.ToInt64(1.05 * throttleLimit); - - //// Don't even start evaluating this until at least two minutes have content have been consumed - //var targetGap = throttleLimit * 120; - var bytesDownloaded = job.BytesDownloaded ?? 0; var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0; var downloadPositionTicks = job.DownloadPositionTicks ?? 0; @@ -95,11 +71,11 @@ namespace MediaBrowser.Api.Playback if (gap < targetGap) { - //Logger.Debug("Not throttling transcoder gap {0} target gap {1}", gap, targetGap); + //_logger.Debug("Not throttling transcoder gap {0} target gap {1}", gap, targetGap); return false; } - //Logger.Debug("Throttling transcoder gap {0} target gap {1}", gap, targetGap); + //_logger.Debug("Throttling transcoder gap {0} target gap {1}", gap, targetGap); return true; } @@ -120,21 +96,21 @@ namespace MediaBrowser.Api.Playback if (gap < targetGap) { - //Logger.Debug("Not throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded); + //_logger.Debug("Not throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded); return false; } - //Logger.Debug("Throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded); + //_logger.Debug("Throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded); return true; } catch { - //Logger.Error("Error getting output size"); + //_logger.Error("Error getting output size"); } } else { - //Logger.Debug("No throttle data for " + path); + //_logger.Debug("No throttle data for " + path); } return false; diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 8ac7d56d2..6fafc2b46 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -2,7 +2,6 @@ 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; @@ -30,10 +29,9 @@ namespace MediaBrowser.Controller.Drawing /// /// Gets the size of the image. /// - /// The path. - /// The image date modified. + /// The information. /// ImageSize. - ImageSize GetImageSize(string path, DateTime imageDateModified); + ImageSize GetImageSize(ItemImageInfo info); /// /// Adds the parts. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index e9531e057..603cb02e0 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -341,8 +341,8 @@ - + diff --git a/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs b/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs deleted file mode 100644 index 8f03aea0a..000000000 --- a/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MediaBrowser.Model.Sync; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Sync -{ - public interface ICloudSyncProvider - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - - /// - /// Gets the synchronize targets. - /// - /// The user identifier. - /// IEnumerable<SyncTarget>. - IEnumerable GetSyncTargets(string userId); - - /// - /// Transfers the item file. - /// - /// The input file. - /// The path parts. - /// The target. - /// The progress. - /// The cancellation token. - /// Task. - Task SendFile(string inputFile, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); - - /// - /// Gets the file. - /// - /// The path parts. - /// The target. - /// The progress. - /// The cancellation token. - /// Task<Stream>. - Task GetFile(string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); - } -} diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index b3e74ee7d..9ee83acbf 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Sync; using System; +using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -12,21 +13,66 @@ namespace MediaBrowser.Controller.Sync /// Transfers the file. /// /// The input file. - /// The path parts. + /// The path. /// The target. /// The progress. /// The cancellation token. /// Task. - Task SendFile(string inputFile, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + Task SendFile(string inputFile, string path, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + + /// + /// Deletes the file. + /// + /// The path. + /// The target. + /// The cancellation token. + /// Task. + Task DeleteFile(string path, SyncTarget target, CancellationToken cancellationToken); /// /// Gets the file. /// - /// The path parts. + /// The path. /// The target. /// The progress. /// The cancellation token. /// Task<Stream>. - Task GetFile(string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + Task GetFile(string path, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + + /// + /// Gets the full path. + /// + /// The path. + /// The target. + /// System.String. + string GetFullPath(IEnumerable path, SyncTarget target); + + /// + /// Gets the parent directory path. + /// + /// The path. + /// The target. + /// System.String. + string GetParentDirectoryPath(string path, SyncTarget target); + + /// + /// Gets the file system entries. + /// + /// The path. + /// The target. + /// Task<List<DeviceFileInfo>>. + Task> GetFileSystemEntries(string path, SyncTarget target); + + /// + /// Gets the data provider. + /// + /// ISyncDataProvider. + ISyncDataProvider GetDataProvider(); + + /// + /// Gets all synchronize targets. + /// + /// IEnumerable<SyncTarget>. + IEnumerable GetAllSyncTargets(); } } diff --git a/MediaBrowser.Controller/Sync/ISyncDataProvider.cs b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs new file mode 100644 index 000000000..cf698dd3c --- /dev/null +++ b/MediaBrowser.Controller/Sync/ISyncDataProvider.cs @@ -0,0 +1,41 @@ +using MediaBrowser.Model.Sync; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Sync +{ + public interface ISyncDataProvider + { + /// + /// Gets the server item ids. + /// + /// The target. + /// The server identifier. + /// Task<List<System.String>>. + Task> GetServerItemIds(SyncTarget target, string serverId); + + /// + /// Adds the or update. + /// + /// The target. + /// The item. + /// Task. + Task AddOrUpdate(SyncTarget target, LocalItem item); + + /// + /// Deletes the specified identifier. + /// + /// The target. + /// The identifier. + /// Task. + Task Delete(SyncTarget target, string id); + + /// + /// Gets the specified identifier. + /// + /// The target. + /// The identifier. + /// Task<LocalItem>. + Task Get(SyncTarget target, string id); + } +} diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs index 6f24eac1a..ba6e54d45 100644 --- a/MediaBrowser.Controller/Sync/ISyncProvider.cs +++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs @@ -18,13 +18,6 @@ namespace MediaBrowser.Controller.Sync /// The user identifier. /// IEnumerable<SyncTarget>. IEnumerable GetSyncTargets(string userId); - - /// - /// Gets the device profile. - /// - /// The target. - /// DeviceProfile. - DeviceProfile GetDeviceProfile(SyncTarget target); } public interface IHasUniqueTargetIds diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index af7c8dbed..b2eedad7c 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -930,7 +930,7 @@ namespace MediaBrowser.Dlna.Didl try { - var size = _imageProcessor.GetImageSize(imageInfo.Path, imageInfo.DateModified); + var size = _imageProcessor.GetImageSize(imageInfo); width = Convert.ToInt32(size.Width); height = Convert.ToInt32(size.Height); diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 410cdc51f..7efc2cf6f 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -59,6 +59,8 @@ namespace MediaBrowser.Model.Users public string[] EnabledFolders { get; set; } public bool EnableAllFolders { get; set; } + public int InvalidLoginAttemptCount { get; set; } + public UserPolicy() { EnableLiveTvManagement = true; diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs index 3eaef59fb..96160dcc4 100644 --- a/MediaBrowser.Providers/Photos/PhotoProvider.cs +++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.Photos try { - var size = _imageProcessor.GetImageSize(imageInfo.Path, imageInfo.DateModified); + var size = _imageProcessor.GetImageSize(imageInfo); item.Width = Convert.ToInt32(size.Width); item.Height = Convert.ToInt32(size.Height); diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 85eadd73c..c484a60db 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -414,6 +414,11 @@ namespace MediaBrowser.Server.Implementations.Drawing return GetImageSize(path, File.GetLastWriteTimeUtc(path)); } + public ImageSize GetImageSize(ItemImageInfo info) + { + return GetImageSize(info.Path, info.DateModified); + } + /// /// Gets the size of the image. /// @@ -421,7 +426,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// The image date modified. /// ImageSize. /// path - public ImageSize GetImageSize(string path, DateTime imageDateModified) + private ImageSize GetImageSize(string path, DateTime imageDateModified) { if (string.IsNullOrEmpty(path)) { diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index b15809738..75037159c 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1598,14 +1598,11 @@ namespace MediaBrowser.Server.Implementations.Dto var path = imageInfo.Path; - // See if we can avoid a file system lookup by looking for the file in ResolveArgs - var dateModified = imageInfo.DateModified; - ImageSize size; try { - size = _imageProcessor.GetImageSize(path, dateModified); + size = _imageProcessor.GetImageSize(imageInfo); } catch (FileNotFoundException) { diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index dc80778da..5f221a7be 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1114,7 +1114,7 @@ "MessageApplicationUpdated": "Media Browser Server has been updated", "AuthenticationSucceededWithUserName": "{0} successfully authenticated", "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "UserDownloadingItemWithValues": "{0} is downloading {1}", + "UserDownloadingItemWithValues": "{0} is downloading {1}", "UserStartedPlayingItemWithValues": "{0} has started playing {1}", "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", "AppDeviceValues": "App: {0}, Device: {1}", @@ -1369,5 +1369,7 @@ "TabJobs": "Jobs", "TabSyncJobs": "Sync Jobs", "LabelTagFilterMode": "Mode:", - "LabelTagFilterAllowModeHelp": "If allowed tags are used as part of a deeply nested folder structure, content that is tagged will require parent folders to be tagged as well." + "LabelTagFilterAllowModeHelp": "If allowed tags are used as part of a deeply nested folder structure, content that is tagged will require parent folders to be tagged as well.", + "HeaderThisUserIsCurrentlyDisabled": "This user is currently disabled", + "MessageReenableUser": "See below to reenable" } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 5ceaa5f5b..40aaa299f 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -303,8 +303,12 @@ - + + + + + diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs index d35ff8fc4..29ac74e82 100644 --- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs @@ -8,7 +8,7 @@ using System.Linq; namespace MediaBrowser.Server.Implementations.Sync { - public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds + public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncProfile { private readonly IDeviceManager _deviceManager; diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs new file mode 100644 index 000000000..babdf3f80 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -0,0 +1,118 @@ +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Server.Implementations.Sync +{ + public class CloudSyncProfile : DeviceProfile + { + public CloudSyncProfile(bool supportsAc3, bool supportsDca) + { + Name = "Cloud Sync"; + + MaxStreamingBitrate = 20000000; + MaxStaticBitrate = 20000000; + + var mkvAudio = "aac,mp3"; + var mp4Audio = "aac"; + + if (supportsAc3) + { + mkvAudio += ",ac3"; + mp4Audio += ",ac3"; + } + + if (supportsDca) + { + mkvAudio += ",dca"; + } + + DirectPlayProfiles = new[] + { + new DirectPlayProfile + { + Container = "mkv", + VideoCodec = "h264,mpeg4", + AudioCodec = mkvAudio, + Type = DlnaProfileType.Video + }, + new DirectPlayProfile + { + Container = "mp4,mov,m4v", + VideoCodec = "h264,mpeg4", + AudioCodec = mp4Audio, + Type = DlnaProfileType.Video + } + }; + + ContainerProfiles = new ContainerProfile[] { }; + + CodecProfiles = new[] + { + new CodecProfile + { + Type = CodecType.Video, + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoBitDepth, + Value = "8", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Height, + Value = "1080", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.RefFrames, + Value = "12", + IsRequired = false + } + } + } + }; + + SubtitleProfiles = new[] + { + new SubtitleProfile + { + Format = "srt", + Method = SubtitleDeliveryMethod.External + } + }; + + TranscodingProfiles = new[] + { + new TranscodingProfile + { + Container = "mp3", + AudioCodec = "mp3", + Type = DlnaProfileType.Audio, + Context = EncodingContext.Static + }, + + new TranscodingProfile + { + Container = "mp4", + Type = DlnaProfileType.Video, + AudioCodec = "aac", + VideoCodec = "h264", + Context = EncodingContext.Static + }, + + new TranscodingProfile + { + Container = "jpeg", + Type = DlnaProfileType.Photo, + Context = EncodingContext.Static + } + }; + + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs deleted file mode 100644 index 1ef9d4b15..000000000 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProvider.cs +++ /dev/null @@ -1,57 +0,0 @@ -using MediaBrowser.Common; -using MediaBrowser.Controller.Sync; -using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Sync; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Sync -{ - public class CloudSyncProvider : IServerSyncProvider - { - private readonly ICloudSyncProvider[] _providers = {}; - - public CloudSyncProvider(IApplicationHost appHost) - { - _providers = appHost.GetExports().ToArray(); - } - - public IEnumerable GetSyncTargets(string userId) - { - return _providers.SelectMany(i => i.GetSyncTargets(userId)); - } - - public DeviceProfile GetDeviceProfile(SyncTarget target) - { - return new DeviceProfile(); - } - - public string Name - { - get { return "Cloud Sync"; } - } - - private ICloudSyncProvider GetProvider(SyncTarget target) - { - return null; - } - - public Task SendFile(string inputFile, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken) - { - var provider = GetProvider(target); - - return provider.SendFile(inputFile, pathParts, target, progress, cancellationToken); - } - - public Task GetFile(string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken) - { - var provider = GetProvider(target); - - return provider.GetFile(pathParts, target, progress, cancellationToken); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs new file mode 100644 index 000000000..b9008d87e --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncDataProvider.cs @@ -0,0 +1,31 @@ +using MediaBrowser.Controller.Sync; +using MediaBrowser.Model.Sync; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sync.FolderSync +{ + public class FolderSyncDataProvider : ISyncDataProvider + { + public Task> GetServerItemIds(SyncTarget target, string serverId) + { + throw new NotImplementedException(); + } + + public Task AddOrUpdate(SyncTarget target, LocalItem item) + { + throw new NotImplementedException(); + } + + public Task Delete(SyncTarget target, string id) + { + throw new NotImplementedException(); + } + + public Task Get(SyncTarget target, string id) + { + throw new NotImplementedException(); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs new file mode 100644 index 000000000..9cf234106 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sync/FolderSync/FolderSyncProvider.cs @@ -0,0 +1,142 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Sync; +using MediaBrowser.Model.Sync; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sync.FolderSync +{ + public class FolderSyncProvider : IServerSyncProvider + { + private readonly IApplicationPaths _appPaths; + private readonly IUserManager _userManager; + + public FolderSyncProvider(IApplicationPaths appPaths, IUserManager userManager) + { + _appPaths = appPaths; + _userManager = userManager; + } + + public Task SendFile(string inputFile, string path, SyncTarget target, IProgress progress, CancellationToken cancellationToken) + { + return Task.Run(() => File.Copy(inputFile, path, true), cancellationToken); + } + + public Task DeleteFile(string path, SyncTarget target, CancellationToken cancellationToken) + { + return Task.Run(() => File.Delete(path), cancellationToken); + } + + public Task GetFile(string path, SyncTarget target, IProgress progress, CancellationToken cancellationToken) + { + return Task.FromResult((Stream)File.OpenRead(path)); + } + + public string GetFullPath(IEnumerable paths, SyncTarget target) + { + var account = GetSyncAccounts() + .FirstOrDefault(i => string.Equals(i.Id, target.Id, StringComparison.OrdinalIgnoreCase)); + + if (account == null) + { + throw new ArgumentException("Invalid SyncTarget supplied."); + } + + var list = paths.ToList(); + list.Insert(0, account.Path); + + return Path.Combine(list.ToArray()); + } + + public string GetParentDirectoryPath(string path, SyncTarget target) + { + return Path.GetDirectoryName(path); + } + + public Task> GetFileSystemEntries(string path, SyncTarget target) + { + List files; + + try + { + files = new DirectoryInfo(path).EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList(); + } + catch (DirectoryNotFoundException) + { + files = new List(); + } + + return Task.FromResult(files.Select(i => new DeviceFileInfo + { + Name = i.Name, + Path = i.FullName + + }).ToList()); + } + + public ISyncDataProvider GetDataProvider() + { + // If single instances are needed, manage them here + return new FolderSyncDataProvider(); + } + + public string Name + { + get { return "Folder Sync"; } + } + + public IEnumerable GetSyncTargets(string userId) + { + return GetSyncAccounts() + .Where(i => i.UserIds.Contains(userId, StringComparer.OrdinalIgnoreCase)) + .Select(GetSyncTarget); + } + + public IEnumerable GetAllSyncTargets() + { + return GetSyncAccounts().Select(GetSyncTarget); + } + + private SyncTarget GetSyncTarget(SyncAccount account) + { + return new SyncTarget + { + Id = account.Id, + Name = account.Name + }; + } + + private IEnumerable GetSyncAccounts() + { + // Dummy this up + return _userManager + .Users + .Select(i => new SyncAccount + { + Id = i.Id.ToString("N"), + UserIds = new List { i.Id.ToString("N") }, + Path = Path.Combine(_appPaths.DataPath, "foldersync", i.Id.ToString("N")), + Name = i.Name + "'s Folder Sync" + }); + } + + // An internal class to manage all configured Folder Sync accounts for differnet users + class SyncAccount + { + public string Id { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public List UserIds { get; set; } + + public SyncAccount() + { + UserIds = new List(); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs new file mode 100644 index 000000000..b7e9daf49 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sync/IHasSyncProfile.cs @@ -0,0 +1,15 @@ +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Sync; + +namespace MediaBrowser.Server.Implementations.Sync +{ + public interface IHasSyncProfile + { + /// + /// Gets the device profile. + /// + /// The target. + /// DeviceProfile. + DeviceProfile GetDeviceProfile(SyncTarget target); + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 764652bbf..0407510a8 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs @@ -1,10 +1,18 @@ -using MediaBrowser.Common.Progress; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Sync; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Sync; using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -15,22 +23,25 @@ namespace MediaBrowser.Server.Implementations.Sync private readonly ISyncManager _syncManager; private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; - public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost) + public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem) { _logger = logger; _syncManager = syncManager; _appHost = appHost; + _fileSystem = fileSystem; } public async Task Sync(IServerSyncProvider provider, + ISyncDataProvider dataProvider, SyncTarget target, IProgress progress, CancellationToken cancellationToken) { var serverId = _appHost.SystemId; - await SyncData(provider, serverId, target, cancellationToken).ConfigureAwait(false); + await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false); progress.Report(3); var innerProgress = new ActionableProgress(); @@ -40,44 +51,46 @@ namespace MediaBrowser.Server.Implementations.Sync totalProgress += 1; progress.Report(totalProgress); }); - await GetNewMedia(provider, target, serverId, innerProgress, cancellationToken); + await GetNewMedia(provider, dataProvider, target, serverId, innerProgress, cancellationToken); // Do the data sync twice so the server knows what was removed from the device - await SyncData(provider, serverId, target, cancellationToken).ConfigureAwait(false); + await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false); progress.Report(100); } private async Task SyncData(IServerSyncProvider provider, + ISyncDataProvider dataProvider, string serverId, SyncTarget target, CancellationToken cancellationToken) { - //var localIds = await provider.GetServerItemIds(serverId, target, cancellationToken).ConfigureAwait(false); + var localIds = await dataProvider.GetServerItemIds(target, serverId).ConfigureAwait(false); - //var result = await _syncManager.SyncData(new SyncDataRequest - //{ - // TargetId = target.Id, - // LocalItemIds = localIds + var result = await _syncManager.SyncData(new SyncDataRequest + { + TargetId = target.Id, + LocalItemIds = localIds - //}).ConfigureAwait(false); + }).ConfigureAwait(false); - //cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - //foreach (var itemIdToRemove in result.ItemIdsToRemove) - //{ - // try - // { - // await RemoveItem(provider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false); - // } - // catch (Exception ex) - // { - // _logger.ErrorException("Error deleting item from sync target. Id: {0}", ex, itemIdToRemove); - // } - //} + foreach (var itemIdToRemove in result.ItemIdsToRemove) + { + try + { + await RemoveItem(provider, dataProvider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error deleting item from device. Id: {0}", ex, itemIdToRemove); + } + } } private async Task GetNewMedia(IServerSyncProvider provider, + ISyncDataProvider dataProvider, SyncTarget target, string serverId, IProgress progress, @@ -106,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.Sync progress.Report(totalProgress); }); - await GetItem(provider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false); + await GetItem(provider, dataProvider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false); numComplete++; startingPercent = numComplete; @@ -117,6 +130,7 @@ namespace MediaBrowser.Server.Implementations.Sync } private async Task GetItem(IServerSyncProvider provider, + ISyncDataProvider dataProvider, SyncTarget target, string serverId, SyncedItem jobItem, @@ -129,6 +143,8 @@ namespace MediaBrowser.Server.Implementations.Sync var fileTransferProgress = new ActionableProgress(); fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92)); + var localItem = CreateLocalItem(provider, target, libraryItem, serverId, jobItem.OriginalFileName); + await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id); var transferSuccess = false; @@ -136,9 +152,10 @@ namespace MediaBrowser.Server.Implementations.Sync try { - string[] pathParts = GetPathParts(serverId, libraryItem); + await SendFile(provider, internalSyncJobItem.OutputPath, localItem, target, cancellationToken).ConfigureAwait(false); - await SendFile(provider, internalSyncJobItem.OutputPath, pathParts, target, cancellationToken).ConfigureAwait(false); + // Create db record + await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false); progress.Report(92); @@ -164,25 +181,189 @@ namespace MediaBrowser.Server.Implementations.Sync } } - private Task RemoveItem(IServerSyncProvider provider, + private async Task RemoveItem(IServerSyncProvider provider, + ISyncDataProvider dataProvider, string serverId, string itemId, SyncTarget target, CancellationToken cancellationToken) { - return Task.FromResult(true); - //return provider.DeleteItem(serverId, itemId, target, cancellationToken); + var localId = GetLocalId(serverId, itemId); + var localItem = await dataProvider.Get(target, localId); + + if (localItem == null) + { + return; + } + + var files = await GetFiles(provider, localItem, target); + + foreach (var file in files) + { + await provider.DeleteFile(file.Path, target, cancellationToken).ConfigureAwait(false); + } + + await dataProvider.Delete(target, localId).ConfigureAwait(false); + } + + private Task SendFile(IServerSyncProvider provider, string inputPath, LocalItem item, SyncTarget target, CancellationToken cancellationToken) + { + return provider.SendFile(inputPath, item.LocalPath, target, new Progress(), cancellationToken); + } + + private string GetLocalId(string serverId, string itemId) + { + var bytes = Encoding.UTF8.GetBytes(serverId + itemId); + bytes = CreateMD5(bytes); + return BitConverter.ToString(bytes, 0, bytes.Length).Replace("-", string.Empty); + } + + private byte[] CreateMD5(byte[] value) + { + using (var provider = MD5.Create()) + { + return provider.ComputeHash(value); + } } - private string[] GetPathParts(string serverId, BaseItemDto item) + public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncTarget target, BaseItemDto libraryItem, string serverId, string originalFileName) { - return null; + var path = GetDirectoryPath(provider, libraryItem, serverId); + path.Add(GetLocalFileName(provider, libraryItem, originalFileName)); + + var localPath = provider.GetFullPath(path, target); + + foreach (var mediaSource in libraryItem.MediaSources) + { + mediaSource.Path = localPath; + mediaSource.Protocol = MediaProtocol.File; + } + + return new LocalItem + { + Item = libraryItem, + ItemId = libraryItem.Id, + ServerId = serverId, + LocalPath = localPath, + Id = GetLocalId(serverId, libraryItem.Id) + }; } - private async Task SendFile(IServerSyncProvider provider, string inputPath, string[] path, SyncTarget target, CancellationToken cancellationToken) + private List GetDirectoryPath(IServerSyncProvider provider, BaseItemDto item, string serverId) { - await provider.SendFile(inputPath, path, target, new Progress(), cancellationToken) - .ConfigureAwait(false); + var parts = new List + { + serverId + }; + + if (item.IsType("episode")) + { + parts.Add("TV"); + parts.Add(item.SeriesName); + + if (!string.IsNullOrWhiteSpace(item.SeasonName)) + { + parts.Add(item.SeasonName); + } + } + else if (item.IsVideo) + { + parts.Add("Videos"); + parts.Add(item.Name); + } + else if (item.IsAudio) + { + parts.Add("Music"); + + if (!string.IsNullOrWhiteSpace(item.AlbumArtist)) + { + parts.Add(item.AlbumArtist); + } + + if (!string.IsNullOrWhiteSpace(item.Album)) + { + parts.Add(item.Album); + } + } + else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase)) + { + parts.Add("Photos"); + + if (!string.IsNullOrWhiteSpace(item.Album)) + { + parts.Add(item.Album); + } + } + + return parts.Select(i => GetValidFilename(provider, i)).ToList(); + } + + private string GetLocalFileName(IServerSyncProvider provider, BaseItemDto item, string originalFileName) + { + var filename = originalFileName; + + if (string.IsNullOrEmpty(filename)) + { + filename = item.Name; + } + + return GetValidFilename(provider, filename); + } + + private string GetValidFilename(IServerSyncProvider provider, string filename) + { + // We can always add this method to the sync provider if it's really needed + return _fileSystem.GetValidFilename(filename); + } + + private async Task> GetFiles(IServerSyncProvider provider, LocalItem item, SyncTarget target) + { + var path = item.LocalPath; + path = provider.GetParentDirectoryPath(path, target); + + var list = await provider.GetFileSystemEntries(path, target).ConfigureAwait(false); + + var itemFiles = new List(); + + var name = Path.GetFileNameWithoutExtension(item.LocalPath); + + foreach (var file in list.Where(f => f.Name.Contains(name))) + { + var itemFile = new ItemFileInfo + { + Path = file.Path, + Name = file.Name + }; + + if (IsSubtitleFile(file.Name)) + { + itemFile.Type = ItemFileType.Subtitles; + } + else if (!IsImageFile(file.Name)) + { + itemFile.Type = ItemFileType.Media; + } + + itemFiles.Add(itemFile); + } + + return itemFiles; + } + + private static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".webp" }; + private bool IsImageFile(string path) + { + var ext = Path.GetExtension(path) ?? string.Empty; + + return SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); + } + + private static readonly string[] SupportedSubtitleExtensions = { ".srt", ".vtt" }; + private bool IsSubtitleFile(string path) + { + var ext = Path.GetExtension(path) ?? string.Empty; + + return SupportedSubtitleExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); } } } diff --git a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs new file mode 100644 index 000000000..cbfa82f1d --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs @@ -0,0 +1,69 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Progress; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Sync; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Sync; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sync +{ + public class MultiProviderSync + { + private readonly ISyncManager _syncManager; + private readonly IServerApplicationHost _appHost; + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + + public MultiProviderSync(ISyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem) + { + _syncManager = syncManager; + _appHost = appHost; + _logger = logger; + _fileSystem = fileSystem; + } + + public async Task Sync(IEnumerable providers, IProgress progress, CancellationToken cancellationToken) + { + var targets = providers + .SelectMany(i => i.GetAllSyncTargets().Select(t => new Tuple(i, t))) + .ToList(); + + var numComplete = 0; + double startingPercent = 0; + double percentPerItem = 1; + if (targets.Count > 0) + { + percentPerItem /= targets.Count; + } + + foreach (var target in targets) + { + cancellationToken.ThrowIfCancellationRequested(); + + var currentPercent = startingPercent; + var innerProgress = new ActionableProgress(); + innerProgress.RegisterAction(pct => + { + var totalProgress = pct * percentPerItem; + totalProgress += currentPercent; + progress.Report(totalProgress); + }); + + await new MediaSync(_logger, _syncManager, _appHost, _fileSystem) + .Sync(target.Item1, target.Item1.GetDataProvider(), target.Item2, innerProgress, cancellationToken) + .ConfigureAwait(false); + + numComplete++; + startingPercent = numComplete; + startingPercent /= targets.Count; + startingPercent *= 100; + progress.Report(startingPercent); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index a2fd92bf5..912967020 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -429,13 +429,6 @@ namespace MediaBrowser.Server.Implementations.Sync return (providerId + "-" + target.Id).GetMD5().ToString("N"); } - private ISyncProvider GetSyncProvider(SyncTarget target) - { - var providerId = target.Id.Split(new[] { '-' }, 2).First(); - - return _providers.First(i => string.Equals(providerId, GetSyncProviderId(i))); - } - private string GetSyncProviderId(ISyncProvider provider) { return (provider.GetType().Name).GetMD5().ToString("N"); @@ -547,7 +540,7 @@ namespace MediaBrowser.Server.Implementations.Sync { if (string.Equals(target.Id, targetId, StringComparison.OrdinalIgnoreCase)) { - return provider.GetDeviceProfile(target); + return GetDeviceProfile(provider, target); } } } @@ -555,6 +548,18 @@ namespace MediaBrowser.Server.Implementations.Sync return null; } + public DeviceProfile GetDeviceProfile(ISyncProvider provider, SyncTarget target) + { + var hasProfile = provider as IHasSyncProfile; + + if (hasProfile != null) + { + return hasProfile.GetDeviceProfile(target); + } + + return new CloudSyncProfile(true, false); + } + public async Task ReportSyncJobItemTransferred(string id) { var jobItem = _repo.GetJobItem(id); -- cgit v1.2.3 From 0d8636d859cef5d0be4a723402926f05499210d7 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 2 Mar 2015 00:16:29 -0500 Subject: update image magick sharp --- MediaBrowser.Controller/Library/IUserManager.cs | 1 + .../Notifications/NotificationType.cs | 3 +- .../Devices/CameraUploadsFolder.cs | 6 ++++ .../Drawing/ImageProcessor.cs | 40 +++------------------- .../EntryPoints/ActivityLogEntryPoint.cs | 12 +++++++ .../EntryPoints/Notifications/Notifications.cs | 18 +++++++++- .../Library/UserManager.cs | 13 +++++++ .../Localization/Server/server.json | 11 ++++++ .../MediaBrowser.Server.Implementations.csproj | 2 +- .../Notifications/CoreNotificationTypes.cs | 11 ++++++ .../Session/SessionManager.cs | 21 +++++++++--- .../packages.config | 2 +- .../MediaBrowser.ServerApplication.csproj | 2 +- MediaBrowser.ServerApplication/packages.config | 2 +- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 1 + .../MediaBrowser.WebDashboard.csproj | 9 +++++ MediaBrowser.sln | 3 ++ 17 files changed, 111 insertions(+), 46 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 8119e5afb..a167cdbed 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -34,6 +34,7 @@ namespace MediaBrowser.Controller.Library event EventHandler> UserCreated; event EventHandler> UserConfigurationUpdated; event EventHandler> UserPasswordChanged; + event EventHandler> UserLockedOut; /// /// Gets a User by Id diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs index 269e27a4f..f5e3624f0 100644 --- a/MediaBrowser.Model/Notifications/NotificationType.cs +++ b/MediaBrowser.Model/Notifications/NotificationType.cs @@ -19,6 +19,7 @@ namespace MediaBrowser.Model.Notifications NewLibraryContentMultiple, ServerRestartRequired, TaskFailed, - CameraImageUploaded + CameraImageUploaded, + UserLockedOut } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs index 2fe5d8f74..566f4c5f4 100644 --- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs +++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; +using System; using System.IO; using System.Linq; @@ -14,6 +15,11 @@ namespace MediaBrowser.Server.Implementations.Devices public override bool IsVisible(User user) { + if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase)) + { + return false; + } + return GetChildren(user, true).Any() && base.IsVisible(user); } diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index c484a60db..180faa6bb 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -350,9 +350,9 @@ namespace MediaBrowser.Server.Implementations.Drawing } /// - /// Increment this when indicator drawings change + /// Increment this when there's a change requiring caches to be invalidated /// - private const string IndicatorVersion = "2"; + private const string Version = "3"; /// /// Gets the cache file path based on a set of parameters @@ -371,29 +371,19 @@ namespace MediaBrowser.Server.Implementations.Drawing filename += "f=" + format; - var hasIndicator = false; - if (addPlayedIndicator) { filename += "pl=true"; - hasIndicator = true; } if (percentPlayed > 0) { filename += "p=" + percentPlayed; - hasIndicator = true; } if (unwatchedCount.HasValue) { filename += "p=" + unwatchedCount.Value; - hasIndicator = true; - } - - if (hasIndicator) - { - filename += "iv=" + IndicatorVersion; } if (!string.IsNullOrEmpty(backgroundColor)) @@ -401,6 +391,8 @@ namespace MediaBrowser.Server.Implementations.Drawing filename += "b=" + backgroundColor; } + filename += "v=" + Version; + return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLower()); } @@ -671,30 +663,6 @@ namespace MediaBrowser.Server.Implementations.Drawing return enhancedImagePath; } - private ImageFormat GetFormat(string path) - { - var extension = Path.GetExtension(path); - - if (string.Equals(extension, ".png", StringComparison.OrdinalIgnoreCase)) - { - return ImageFormat.Png; - } - if (string.Equals(extension, ".gif", StringComparison.OrdinalIgnoreCase)) - { - return ImageFormat.Gif; - } - if (string.Equals(extension, ".webp", StringComparison.OrdinalIgnoreCase)) - { - return ImageFormat.Webp; - } - if (string.Equals(extension, ".bmp", StringComparison.OrdinalIgnoreCase)) - { - return ImageFormat.Bmp; - } - - return ImageFormat.Jpg; - } - /// /// Executes the image enhancers. /// diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs index 0b0661321..28883e9a2 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs @@ -86,6 +86,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints _userManager.UserPasswordChanged += _userManager_UserPasswordChanged; _userManager.UserDeleted += _userManager_UserDeleted; _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated; + _userManager.UserLockedOut += _userManager_UserLockedOut; //_config.ConfigurationUpdated += _config_ConfigurationUpdated; //_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; @@ -95,6 +96,16 @@ namespace MediaBrowser.Server.Implementations.EntryPoints _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; } + void _userManager_UserLockedOut(object sender, GenericEventArgs e) + { + CreateLogEntry(new ActivityLogEntry + { + Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name), + Type = "UserLockedOut", + UserId = e.Argument.Id.ToString("N") + }); + } + void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) { CreateLogEntry(new ActivityLogEntry @@ -482,6 +493,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints _userManager.UserPasswordChanged -= _userManager_UserPasswordChanged; _userManager.UserDeleted -= _userManager_UserDeleted; _userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated; + _userManager.UserLockedOut -= _userManager_UserLockedOut; _config.ConfigurationUpdated -= _config_ConfigurationUpdated; _config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated; diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs index 37bca4ddb..f6a35973b 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs @@ -78,6 +78,22 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged; _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; _deviceManager.CameraImageUploaded +=_deviceManager_CameraImageUploaded; + + _userManager.UserLockedOut += _userManager_UserLockedOut; + } + + async void _userManager_UserLockedOut(object sender, GenericEventArgs e) + { + var type = NotificationType.UserLockedOut.ToString(); + + var notification = new NotificationRequest + { + NotificationType = type + }; + + notification.Variables["UserName"] = e.Argument.Name; + + await SendNotification(notification).ConfigureAwait(false); } async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs e) @@ -235,7 +251,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications return; } - var notification = new NotificationRequest { NotificationType = type @@ -471,6 +486,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated; _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded; + _userManager.UserLockedOut -= _userManager_UserLockedOut; } private void DisposeLibraryUpdateTimer() diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 846bad214..00c674436 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -97,6 +97,7 @@ namespace MediaBrowser.Server.Implementations.Library /// public event EventHandler> UserUpdated; public event EventHandler> UserConfigurationUpdated; + public event EventHandler> UserLockedOut; /// /// Called when [user updated]. @@ -281,13 +282,25 @@ namespace MediaBrowser.Server.Implementations.Library 3 : 5; + var fireLockout = false; + if (newValue >= maxCount) { _logger.Debug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture)); user.Policy.IsDisabled = true; + + fireLockout = true; } await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false); + + if (fireLockout) + { + if (UserLockedOut != null) + { + EventHelper.FireEventIfNotNull(UserLockedOut, this, new GenericEventArgs(user), _logger); + } + } } } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 5f221a7be..2f593efcd 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -48,8 +48,10 @@ "LabelDashboardSourcePathHelp": "If running the server from source, specify the path to the dashboard-ui folder. All web client files will be served from this location.", "ButtonConvertMedia": "Convert media", "ButtonOrganize": "Organize", + "LabelPinCode": "Pin code:", "ButtonOk": "Ok", "ButtonCancel": "Cancel", + "ButtonExit": "Exit", "ButtonNew": "New", "HeaderTV": "TV", "HeaderAudio": "Audio", @@ -57,6 +59,12 @@ "HeaderPaths": "Paths", "CategorySync": "Sync", "HeaderEasyPinCode": "Easy Pin Code", + "HeaderGrownupsOnly": "Grown-ups Only!", + "DividerOr": "-- or --", + "HeaderToAccessPleaseEnterEasyPinCode": "To access, please enter your easy pin code", + "KidsModeAdultInstruction": "Click the lock icon in the bottom right to configure or leave kids mode. Your pin code will be required.", + "ButtonConfigurePinCode": "Configure pin code", + "HeaderAdultsReadHere": "Adults Read Here!", "RegisterWithPayPal": "Register with PayPal", "HeaderSyncRequiresSupporterMembership": "Sync Requires a Supporter Membership", "HeaderEnjoyDayTrial": "Enjoy a 14 Day Free Trial", @@ -670,6 +678,7 @@ "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)", "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionUserLockedOut": "User locked out", "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", "NotificationOptionServerRestartRequired": "Server restart required", "LabelNotificationEnabled": "Enable this notification", @@ -1061,6 +1070,7 @@ "OptionBox": "Box", "OptionBoxRear": "Box rear", "OptionDisc": "Disc", + "OptionIcon": "Icon", "OptionLogo": "Logo", "OptionMenu": "Menu", "OptionScreenshot": "Screenshot", @@ -1105,6 +1115,7 @@ "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "LabelRunningTimeValue": "Running time: {0}", "LabelIpAddressValue": "Ip address: {0}", + "UserLockedOutWithName": "User {0} has been locked out", "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", "UserCreatedWithName": "User {0} has been created", "UserPasswordChangedWithName": "Password has been changed for user {0}", diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index fdd53b907..7833058f4 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -47,7 +47,7 @@ False - ..\packages\ImageMagickSharp.1.0.0.4\lib\net45\ImageMagickSharp.dll + ..\packages\ImageMagickSharp.1.0.0.5\lib\net45\ImageMagickSharp.dll False diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs index d8acbe06c..a33fe2147 100644 --- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs +++ b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs @@ -143,6 +143,13 @@ namespace MediaBrowser.Server.Implementations.Notifications Type = NotificationType.CameraImageUploaded.ToString(), DefaultTitle = "A new camera image has been uploaded from {DeviceName}.", Variables = new List{"DeviceName"} + }, + + new NotificationTypeInfo + { + Type = NotificationType.UserLockedOut.ToString(), + DefaultTitle = "{UserName} has been locked out.", + Variables = new List{"UserName"} } }; @@ -185,6 +192,10 @@ namespace MediaBrowser.Server.Implementations.Notifications { note.Category = _localization.GetLocalizedString("CategorySync"); } + else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1) + { + note.Category = _localization.GetLocalizedString("CategoryUser"); + } else { note.Category = _localization.GetLocalizedString("CategorySystem"); diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index d02ef9d27..3ffbf5cb9 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -399,7 +399,7 @@ namespace MediaBrowser.Server.Implementations.Session Client = clientType, DeviceId = deviceId, ApplicationVersion = appVersion, - Id = Guid.NewGuid().ToString("N") + Id = key.GetMD5().ToString("N") }; sessionInfo.DeviceName = deviceName; @@ -798,6 +798,19 @@ namespace MediaBrowser.Server.Implementations.Session return session; } + private SessionInfo GetSessionToRemoteControl(string sessionId) + { + // Accept either device id or session id + var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId)); + + if (session == null) + { + throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + } + + return session; + } + public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken) { var generalCommand = new GeneralCommand @@ -818,7 +831,7 @@ namespace MediaBrowser.Server.Implementations.Session public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken) { - var session = GetSession(sessionId); + var session = GetSessionToRemoteControl(sessionId); var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); @@ -828,7 +841,7 @@ namespace MediaBrowser.Server.Implementations.Session public Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken) { - var session = GetSession(sessionId); + var session = GetSessionToRemoteControl(sessionId); var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null; @@ -955,7 +968,7 @@ namespace MediaBrowser.Server.Implementations.Session public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken) { - var session = GetSession(sessionId); + var session = GetSessionToRemoteControl(sessionId); var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index c97792fe0..b83bee17d 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -1,6 +1,6 @@  - + diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index fcda32a33..019f1e977 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -62,7 +62,7 @@ False - ..\packages\ImageMagickSharp.1.0.0.4\lib\net45\ImageMagickSharp.dll + ..\packages\ImageMagickSharp.1.0.0.5\lib\net45\ImageMagickSharp.dll ..\packages\MediaBrowser.IsoMounting.3.0.69\lib\net45\MediaBrowser.IsoMounter.dll diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index 5e4e31b86..cf01ed666 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 1e188ceae..8328cf8ab 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -421,6 +421,7 @@ namespace MediaBrowser.WebDashboard.Api "itembynamedetailpage.js", "itemdetailpage.js", "itemlistpage.js", + "kids.js", "librarypathmapping.js", "reports.js", "librarysettings.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 812deabe1..fa6413b8b 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -87,6 +87,9 @@ + + PreserveNewest + PreserveNewest @@ -114,6 +117,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -129,6 +135,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 143a3da41..f73971374 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -520,4 +520,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal -- cgit v1.2.3 From 3788033dcd99c8e8616db309a4ddd232a9dc801e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 6 Mar 2015 01:42:59 -0500 Subject: don't allow image processing after disposing --- .../Drawing/ImageProcessor.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 180faa6bb..64c812e1b 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -202,6 +202,8 @@ namespace MediaBrowser.Server.Implementations.Drawing try { + CheckDisposed(); + var newWidth = Convert.ToInt32(newSize.Width); var newHeight = Convert.ToInt32(newSize.Height); @@ -448,6 +450,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// ImageSize. private ImageSize GetImageSizeInternal(string path) { + CheckDisposed(); var size = ImageHeader.GetDimensions(path, _logger, _fileSystem); StartSaveImageSizeTimer(); @@ -793,10 +796,20 @@ namespace MediaBrowser.Server.Implementations.Drawing }); } + private bool _disposed; public void Dispose() { + _disposed = true; Wand.CloseEnvironment(); _saveImageSizeTimer.Dispose(); } + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } } } -- cgit v1.2.3 From 23a062103a90caa70963f12fd06b035b8e122305 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 6 Mar 2015 12:50:14 -0500 Subject: move image magick files --- .../Drawing/ImageHeader.cs | 28 +----- .../Drawing/ImageProcessor.cs | 32 +++++- MediaBrowser.ServerApplication/MainStartup.cs | 2 +- .../MediaBrowser.ServerApplication.csproj | 109 +++++++++++---------- 4 files changed, 88 insertions(+), 83 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs index 6287d0bb8..7117482c8 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageHeader.cs @@ -1,5 +1,4 @@ -using ImageMagickSharp; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Logging; using System; @@ -47,32 +46,13 @@ namespace MediaBrowser.Server.Implementations.Drawing /// The image was of an unrecognised format. public static ImageSize GetDimensions(string path, ILogger logger, IFileSystem fileSystem) { - try + using (var fs = File.OpenRead(path)) { - using (var fs = File.OpenRead(path)) + using (var binaryReader = new BinaryReader(fs)) { - using (var binaryReader = new BinaryReader(fs)) - { - return GetDimensions(binaryReader); - } + return GetDimensions(binaryReader); } } - catch - { - logger.Info("Failed to read image header for {0}. Doing it the slow way.", path); - } - - using (var wand = new MagickWand()) - { - wand.PingImage(path); - var img = wand.CurrentImage; - - return new ImageSize - { - Width = img.Width, - Height = img.Height - }; - } } /// diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 64c812e1b..80cfd7f3e 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -203,7 +203,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { CheckDisposed(); - + var newWidth = Convert.ToInt32(newSize.Width); var newHeight = Convert.ToInt32(newSize.Height); @@ -329,7 +329,7 @@ namespace MediaBrowser.Server.Implementations.Drawing try { Directory.CreateDirectory(Path.GetDirectoryName(croppedImagePath)); - + using (var wand = new MagickWand(originalImagePath)) { wand.CurrentImage.TrimImage(10); @@ -450,12 +450,34 @@ namespace MediaBrowser.Server.Implementations.Drawing /// ImageSize. private ImageSize GetImageSizeInternal(string path) { - CheckDisposed(); - var size = ImageHeader.GetDimensions(path, _logger, _fileSystem); + ImageSize size; + + try + { + size = ImageHeader.GetDimensions(path, _logger, _fileSystem); + } + catch + { + _logger.Info("Failed to read image header for {0}. Doing it the slow way.", path); + + CheckDisposed(); + + using (var wand = new MagickWand()) + { + wand.PingImage(path); + var img = wand.CurrentImage; + + size = new ImageSize + { + Width = img.Width, + Height = img.Height + }; + } + } StartSaveImageSizeTimer(); - return new ImageSize { Width = size.Width, Height = size.Height }; + return size; } private readonly Timer _saveImageSizeTimer; diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 4bf51bc6b..598ec1fd7 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.ServerApplication var applicationPath = currentProcess.MainModule.FileName; - Wand.SetMagickCoderModulePath(Path.Combine(Path.GetDirectoryName(applicationPath), "ImageMagickCoders", "x86")); + //Wand.SetMagickCoderModulePath(Path.Combine(Path.GetDirectoryName(applicationPath), "ImageMagickCoders", "x86")); var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService); var logManager = new NlogManager(appPaths.LogDirectoryPath, "server"); diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 7c23f9341..fb889e4f2 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -239,163 +239,163 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest @@ -455,6 +455,9 @@ MediaBrowser.XbmcMetadata + + + -- cgit v1.2.3 From 2caf01f43b7153127f8d26b26da3f762cbf321d4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 8 Mar 2015 23:56:42 -0400 Subject: embed fonts --- .../Drawing/ImageProcessor.cs | 4 +- .../Drawing/PlayedIndicatorDrawer.cs | 46 +++++++++++++++++++++- .../Drawing/UnplayedCountIndicator.cs | 16 ++++++-- .../MediaBrowser.Server.Implementations.csproj | 2 + 4 files changed, 61 insertions(+), 7 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 80cfd7f3e..cb6058f3f 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -282,13 +282,13 @@ namespace MediaBrowser.Server.Implementations.Drawing { var currentImageSize = new ImageSize(imageWidth, imageHeight); - new PlayedIndicatorDrawer().DrawPlayedIndicator(wand, currentImageSize); + new PlayedIndicatorDrawer(_appPaths).DrawPlayedIndicator(wand, currentImageSize); } else if (options.UnplayedCount.HasValue) { var currentImageSize = new ImageSize(imageWidth, imageHeight); - new UnplayedCountIndicator().DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value); + new UnplayedCountIndicator(_appPaths).DrawUnplayedCountIndicator(wand, currentImageSize, options.UnplayedCount.Value); } if (options.PercentPlayed > 0) diff --git a/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs b/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs index 6dd0b0fe7..359065cc2 100644 --- a/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs +++ b/MediaBrowser.Server.Implementations/Drawing/PlayedIndicatorDrawer.cs @@ -1,5 +1,8 @@ using ImageMagickSharp; +using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Drawing; +using System; +using System.IO; namespace MediaBrowser.Server.Implementations.Drawing { @@ -8,6 +11,13 @@ namespace MediaBrowser.Server.Implementations.Drawing private const int FontSize = 52; private const int OffsetFromTopRightCorner = 38; + private readonly IApplicationPaths _appPaths; + + public PlayedIndicatorDrawer(IApplicationPaths appPaths) + { + _appPaths = appPaths; + } + public void DrawPlayedIndicator(MagickWand wand, ImageSize imageSize) { var x = imageSize.Width - OffsetFromTopRightCorner; @@ -24,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Drawing pixel.Opacity = 0; pixel.Color = "white"; draw.FillColor = pixel; - draw.Font = "Webdings"; + draw.Font = ExtractFont("webdings.ttf", _appPaths); draw.FontSize = FontSize; draw.FontStyle = FontStyleType.NormalStyle; draw.TextAlignment = TextAlignType.CenterAlign; @@ -35,8 +45,42 @@ namespace MediaBrowser.Server.Implementations.Drawing draw.FillColor = pixel; wand.CurrentImage.DrawImage(draw); } + } + } + + internal static string ExtractFont(string name, IApplicationPaths paths) + { + var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name); + + if (File.Exists(filePath)) + { + return filePath; + } + + var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name; + var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf"); + Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); + + using (var stream = typeof(PlayedIndicatorDrawer).Assembly.GetManifestResourceStream(namespacePath)) + { + using (var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + stream.CopyTo(fileStream); + } + } + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + + try + { + File.Copy(tempPath, filePath, false); } + catch (IOException) + { + + } + + return tempPath; } } } diff --git a/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs b/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs index c10084c9b..65212a315 100644 --- a/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs +++ b/MediaBrowser.Server.Implementations/Drawing/UnplayedCountIndicator.cs @@ -1,6 +1,7 @@ -using System.Globalization; -using ImageMagickSharp; +using ImageMagickSharp; +using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Drawing; +using System.Globalization; namespace MediaBrowser.Server.Implementations.Drawing { @@ -8,6 +9,13 @@ namespace MediaBrowser.Server.Implementations.Drawing { private const int OffsetFromTopRightCorner = 38; + private readonly IApplicationPaths _appPaths; + + public UnplayedCountIndicator(IApplicationPaths appPaths) + { + _appPaths = appPaths; + } + public void DrawUnplayedCountIndicator(MagickWand wand, ImageSize imageSize, int count) { var x = imageSize.Width - OffsetFromTopRightCorner; @@ -25,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Drawing pixel.Opacity = 0; pixel.Color = "white"; draw.FillColor = pixel; - draw.Font = "Sans-Serif"; + draw.Font = PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths); draw.FontStyle = FontStyleType.NormalStyle; draw.TextAlignment = TextAlignType.CenterAlign; draw.FontWeight = FontWeightType.RegularStyle; @@ -36,7 +44,7 @@ namespace MediaBrowser.Server.Implementations.Drawing if (text.Length == 1) { - x += 2; + x += 1; } else if (text.Length == 2) { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 923e57b9b..f645951a7 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -426,6 +426,8 @@ + + -- cgit v1.2.3 From 44d0e30e9de8d2eef957eca1f97ee3fd5aa71810 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 17 Mar 2015 12:47:56 -0400 Subject: perform webp test at startup --- .../Drawing/ImageProcessor.cs | 54 ++++++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs') diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index cb6058f3f..79e5a0cf0 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -130,7 +130,44 @@ namespace MediaBrowser.Server.Implementations.Drawing public ImageFormat[] GetSupportedImageOutputFormats() { - return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png }; + if (_webpAvailable) + { + return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png }; + } + return new[] { ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png }; + } + + private bool _webpAvailable = true; + private void TestWebp() + { + try + { + var tmpPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".webp"); + Directory.CreateDirectory(Path.GetDirectoryName(tmpPath)); + + using (var wand = new MagickWand(1, 1, new PixelWand("none", 1))) + { + wand.SaveImage(tmpPath); + } + } + catch (Exception ex) + { + _logger.ErrorException("Error loading webp: ", ex); + _webpAvailable = false; + } + } + + private void LogImageMagickVersionVersion() + { + try + { + _logger.Info("ImageMagick version: " + Wand.VersionString); + } + catch (Exception ex) + { + _logger.ErrorException("Error loading ImageMagick: ", ex); + } + TestWebp(); } public async Task ProcessImage(ImageProcessingOptions options) @@ -179,7 +216,8 @@ namespace MediaBrowser.Server.Implementations.Drawing var quality = options.Quality ?? 90; - var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor); + var outputFormat = GetOutputFormat(options.OutputFormat); + var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor); var semaphore = GetLock(cacheFilePath); @@ -250,16 +288,14 @@ namespace MediaBrowser.Server.Implementations.Drawing } } - private void LogImageMagickVersionVersion() + private ImageFormat GetOutputFormat(ImageFormat requestedFormat) { - try - { - _logger.Info("ImageMagick version: " + Wand.VersionString); - } - catch (Exception ex) + if (requestedFormat == ImageFormat.Webp && !_webpAvailable) { - _logger.ErrorException("Error loading ImageMagick: ", ex); + return ImageFormat.Png; } + + return requestedFormat; } /// -- cgit v1.2.3