|
@@ -7,6 +7,7 @@ using MediaBrowser.Controller.Providers;
|
|
using MediaBrowser.Model.Drawing;
|
|
using MediaBrowser.Model.Drawing;
|
|
using MediaBrowser.Model.Entities;
|
|
using MediaBrowser.Model.Entities;
|
|
using MediaBrowser.Model.Logging;
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
+using MediaBrowser.Model.Serialization;
|
|
using System;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
@@ -24,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Class ImageProcessor
|
|
/// Class ImageProcessor
|
|
/// </summary>
|
|
/// </summary>
|
|
- public class ImageProcessor : IImageProcessor
|
|
|
|
|
|
+ public class ImageProcessor : IImageProcessor, IDisposable
|
|
{
|
|
{
|
|
/// <summary>
|
|
/// <summary>
|
|
/// The us culture
|
|
/// The us culture
|
|
@@ -34,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
/// <summary>
|
|
/// <summary>
|
|
/// The _cached imaged sizes
|
|
/// The _cached imaged sizes
|
|
/// </summary>
|
|
/// </summary>
|
|
- private readonly ConcurrentDictionary<string, ImageSize> _cachedImagedSizes = new ConcurrentDictionary<string, ImageSize>();
|
|
|
|
|
|
+ private readonly ConcurrentDictionary<Guid, ImageSize> _cachedImagedSizes;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Gets the list of currently registered image processors
|
|
/// Gets the list of currently registered image processors
|
|
@@ -49,21 +50,41 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
private readonly ILogger _logger;
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
private readonly IFileSystem _fileSystem;
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
+ private readonly IJsonSerializer _jsonSerializer;
|
|
|
|
+ private readonly IServerApplicationPaths _appPaths;
|
|
|
|
|
|
private readonly string _imageSizeCachePath;
|
|
private readonly string _imageSizeCachePath;
|
|
private readonly string _croppedWhitespaceImageCachePath;
|
|
private readonly string _croppedWhitespaceImageCachePath;
|
|
private readonly string _enhancedImageCachePath;
|
|
private readonly string _enhancedImageCachePath;
|
|
private readonly string _resizedImageCachePath;
|
|
private readonly string _resizedImageCachePath;
|
|
|
|
|
|
- public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem)
|
|
|
|
|
|
+ public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
|
|
{
|
|
{
|
|
_logger = logger;
|
|
_logger = logger;
|
|
_fileSystem = fileSystem;
|
|
_fileSystem = fileSystem;
|
|
|
|
+ _jsonSerializer = jsonSerializer;
|
|
|
|
+ _appPaths = appPaths;
|
|
|
|
|
|
_imageSizeCachePath = Path.Combine(appPaths.ImageCachePath, "image-sizes");
|
|
_imageSizeCachePath = Path.Combine(appPaths.ImageCachePath, "image-sizes");
|
|
_croppedWhitespaceImageCachePath = Path.Combine(appPaths.ImageCachePath, "cropped-images");
|
|
_croppedWhitespaceImageCachePath = Path.Combine(appPaths.ImageCachePath, "cropped-images");
|
|
_enhancedImageCachePath = Path.Combine(appPaths.ImageCachePath, "enhanced-images");
|
|
_enhancedImageCachePath = Path.Combine(appPaths.ImageCachePath, "enhanced-images");
|
|
_resizedImageCachePath = Path.Combine(appPaths.ImageCachePath, "resized-images");
|
|
_resizedImageCachePath = Path.Combine(appPaths.ImageCachePath, "resized-images");
|
|
|
|
+
|
|
|
|
+ _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
|
|
|
|
+
|
|
|
|
+ Dictionary<Guid, ImageSize> sizeDictionary;
|
|
|
|
+
|
|
|
|
+ try
|
|
|
|
+ {
|
|
|
|
+ sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile);
|
|
|
|
+ }
|
|
|
|
+ catch (IOException)
|
|
|
|
+ {
|
|
|
|
+ // No biggie
|
|
|
|
+ sizeDictionary = new Dictionary<Guid, ImageSize>();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
|
|
}
|
|
}
|
|
|
|
|
|
public void AddParts(IEnumerable<IImageEnhancer> enhancers)
|
|
public void AddParts(IEnumerable<IImageEnhancer> enhancers)
|
|
@@ -84,7 +105,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
}
|
|
}
|
|
|
|
|
|
var originalImagePath = options.OriginalImagePath;
|
|
var originalImagePath = options.OriginalImagePath;
|
|
-
|
|
|
|
|
|
+
|
|
if (options.HasDefaultOptions())
|
|
if (options.HasDefaultOptions())
|
|
{
|
|
{
|
|
// Just spit out the original file if all the options are default
|
|
// Just spit out the original file if all the options are default
|
|
@@ -125,7 +146,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
var quality = options.Quality ?? 90;
|
|
var quality = options.Quality ?? 90;
|
|
|
|
|
|
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.BackgroundColor);
|
|
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.BackgroundColor);
|
|
@@ -485,11 +506,13 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
|
|
|
|
ImageSize size;
|
|
ImageSize size;
|
|
|
|
|
|
- if (!_cachedImagedSizes.TryGetValue(name, out size))
|
|
|
|
|
|
+ var cacheHash = name.GetMD5();
|
|
|
|
+
|
|
|
|
+ if (!_cachedImagedSizes.TryGetValue(cacheHash, out size))
|
|
{
|
|
{
|
|
- size = GetImageSizeInternal(name, path);
|
|
|
|
|
|
+ size = GetImageSizeInternal(path);
|
|
|
|
|
|
- _cachedImagedSizes.AddOrUpdate(name, size, (keyName, oldValue) => size);
|
|
|
|
|
|
+ _cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size);
|
|
}
|
|
}
|
|
|
|
|
|
return size;
|
|
return size;
|
|
@@ -498,62 +521,47 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Gets the image size internal.
|
|
/// Gets the image size internal.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <param name="cacheKey">The cache key.</param>
|
|
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="path">The path.</param>
|
|
/// <returns>ImageSize.</returns>
|
|
/// <returns>ImageSize.</returns>
|
|
- private ImageSize GetImageSizeInternal(string cacheKey, string path)
|
|
|
|
|
|
+ private ImageSize GetImageSizeInternal(string path)
|
|
{
|
|
{
|
|
- // Now check the file system cache
|
|
|
|
- var fullCachePath = GetCachePath(_imageSizeCachePath, cacheKey, ".txt");
|
|
|
|
|
|
+ var size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
|
|
|
|
|
- try
|
|
|
|
- {
|
|
|
|
- var result = File.ReadAllText(fullCachePath).Split('|');
|
|
|
|
|
|
+ StartSaveImageSizeTimer();
|
|
|
|
|
|
- return new ImageSize
|
|
|
|
- {
|
|
|
|
- Width = double.Parse(result[0], UsCulture),
|
|
|
|
- Height = double.Parse(result[1], UsCulture)
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- catch (IOException)
|
|
|
|
- {
|
|
|
|
- // Cache file doesn't exist or is currently being written to
|
|
|
|
- }
|
|
|
|
|
|
+ return new ImageSize { Width = size.Width, Height = size.Height };
|
|
|
|
+ }
|
|
|
|
|
|
- var syncLock = GetObjectLock(fullCachePath);
|
|
|
|
|
|
+ private readonly Timer _saveImageSizeTimer;
|
|
|
|
+ private const int SaveImageSizeTimeout = 5000;
|
|
|
|
+ private readonly object _saveImageSizeLock = new object();
|
|
|
|
+ private void StartSaveImageSizeTimer()
|
|
|
|
+ {
|
|
|
|
+ _saveImageSizeTimer.Change(SaveImageSizeTimeout, Timeout.Infinite);
|
|
|
|
+ }
|
|
|
|
|
|
- lock (syncLock)
|
|
|
|
|
|
+ private void SaveImageSizeCallback(object state)
|
|
|
|
+ {
|
|
|
|
+ lock (_saveImageSizeLock)
|
|
{
|
|
{
|
|
try
|
|
try
|
|
{
|
|
{
|
|
- var result = File.ReadAllText(fullCachePath).Split('|');
|
|
|
|
-
|
|
|
|
- return new ImageSize
|
|
|
|
- {
|
|
|
|
- Width = double.Parse(result[0], UsCulture),
|
|
|
|
- Height = double.Parse(result[1], UsCulture)
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- catch (FileNotFoundException)
|
|
|
|
- {
|
|
|
|
- // Cache file doesn't exist no biggie
|
|
|
|
|
|
+ var path = ImageSizeFile;
|
|
|
|
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
|
|
+ _jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
|
|
}
|
|
}
|
|
- catch (DirectoryNotFoundException)
|
|
|
|
|
|
+ catch (Exception ex)
|
|
{
|
|
{
|
|
- // Cache file doesn't exist no biggie
|
|
|
|
|
|
+ _logger.ErrorException("Error saving image size file", ex);
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- var size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
|
|
|
-
|
|
|
|
- var parentPath = Path.GetDirectoryName(fullCachePath);
|
|
|
|
-
|
|
|
|
- Directory.CreateDirectory(parentPath);
|
|
|
|
-
|
|
|
|
- // Update the file system cache
|
|
|
|
- File.WriteAllText(fullCachePath, size.Width.ToString(UsCulture) + @"|" + size.Height.ToString(UsCulture));
|
|
|
|
-
|
|
|
|
- return new ImageSize { Width = size.Width, Height = size.Height };
|
|
|
|
|
|
+ private string ImageSizeFile
|
|
|
|
+ {
|
|
|
|
+ get
|
|
|
|
+ {
|
|
|
|
+ return Path.Combine(_appPaths.DataPath, "imagesizes.json");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -882,5 +890,10 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
|
|
|
|
|
}).ToList();
|
|
}).ToList();
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ public void Dispose()
|
|
|
|
+ {
|
|
|
|
+ _saveImageSizeTimer.Dispose();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|