optimize image processor when gdi can be skipped
This commit is contained in:
parent
d609d0edec
commit
2fc662c9e9
|
@ -1,4 +1,5 @@
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Drawing
|
namespace MediaBrowser.Model.Drawing
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -131,20 +132,77 @@ namespace MediaBrowser.Model.Drawing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct ImageSize
|
public struct ImageSize
|
||||||
{
|
{
|
||||||
|
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
|
private double _height;
|
||||||
|
private double _width;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the height.
|
/// Gets or sets the height.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The height.</value>
|
/// <value>The height.</value>
|
||||||
public double Height { get; set; }
|
public double Height
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_height = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the width.
|
/// Gets or sets the width.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The width.</value>
|
/// <value>The width.</value>
|
||||||
public double Width { get; set; }
|
public double Width
|
||||||
|
{
|
||||||
|
get { return _width; }
|
||||||
|
set { _width = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public bool Equals(ImageSize size)
|
public bool Equals(ImageSize size)
|
||||||
{
|
{
|
||||||
return Width.Equals(size.Width) && Height.Equals(size.Height);
|
return Width.Equals(size.Width) && Height.Equals(size.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("{0}-{1}", Width, Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageSize(string value)
|
||||||
|
{
|
||||||
|
_width = 0;
|
||||||
|
|
||||||
|
_height = 0;
|
||||||
|
|
||||||
|
ParseValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseValue(string value)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
var parts = value.Split('-');
|
||||||
|
|
||||||
|
if (parts.Length == 2)
|
||||||
|
{
|
||||||
|
double val;
|
||||||
|
|
||||||
|
if (double.TryParse(parts[0], NumberStyles.Any, UsCulture, out val))
|
||||||
|
{
|
||||||
|
_width = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (double.TryParse(parts[1], NumberStyles.Any, UsCulture, out val))
|
||||||
|
{
|
||||||
|
_height = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -822,13 +822,18 @@ namespace MediaBrowser.Providers.Movies
|
||||||
|
|
||||||
// genres
|
// genres
|
||||||
// Movies get this from imdb
|
// Movies get this from imdb
|
||||||
if (movieData.genres != null && !movie.LockedFields.Contains(MetadataFields.Genres) && movie is BoxSet)
|
if (movieData.genres != null && !movie.LockedFields.Contains(MetadataFields.Genres))
|
||||||
{
|
{
|
||||||
movie.Genres.Clear();
|
// Only grab them if a boxset or there are no genres.
|
||||||
|
// For movies and trailers we'll use imdb via omdb
|
||||||
foreach (var genre in movieData.genres.Select(g => g.name))
|
if (movie is BoxSet || movie.Genres.Count == 0)
|
||||||
{
|
{
|
||||||
movie.AddGenre(genre);
|
movie.Genres.Clear();
|
||||||
|
|
||||||
|
foreach (var genre in movieData.genres.Select(g => g.name))
|
||||||
|
{
|
||||||
|
movie.AddGenre(genre);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,9 +70,12 @@ namespace MediaBrowser.Providers.Music
|
||||||
/// <returns>Task{System.Boolean}.</returns>
|
/// <returns>Task{System.Boolean}.</returns>
|
||||||
public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
|
public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualLastFmImageProvider.ProviderName).ConfigureAwait(false);
|
if (!item.HasImage(ImageType.Primary))
|
||||||
|
{
|
||||||
|
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualLastFmImageProvider.ProviderName).ConfigureAwait(false);
|
||||||
|
|
||||||
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
|
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
SetLastRefreshed(item, DateTime.UtcNow);
|
SetLastRefreshed(item, DateTime.UtcNow);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -485,11 +506,13 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
||||||
|
|
||||||
ImageSize size;
|
ImageSize size;
|
||||||
|
|
||||||
if (!_cachedImagedSizes.TryGetValue(name, out size))
|
var cacheHash = name.GetMD5();
|
||||||
{
|
|
||||||
size = GetImageSizeInternal(name, path);
|
|
||||||
|
|
||||||
_cachedImagedSizes.AddOrUpdate(name, size, (keyName, oldValue) => size);
|
if (!_cachedImagedSizes.TryGetValue(cacheHash, out size))
|
||||||
|
{
|
||||||
|
size = GetImageSizeInternal(path);
|
||||||
|
|
||||||
|
_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 size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
||||||
var fullCachePath = GetCachePath(_imageSizeCachePath, cacheKey, ".txt");
|
|
||||||
|
|
||||||
try
|
StartSaveImageSizeTimer();
|
||||||
{
|
|
||||||
var result = File.ReadAllText(fullCachePath).Split('|');
|
|
||||||
|
|
||||||
return new ImageSize
|
return new ImageSize { Width = size.Width, Height = size.Height };
|
||||||
{
|
}
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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('|');
|
var path = ImageSizeFile;
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
return new ImageSize
|
_jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
|
||||||
{
|
|
||||||
Width = double.Parse(result[0], UsCulture),
|
|
||||||
Height = double.Parse(result[1], UsCulture)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Cache file doesn't exist no biggie
|
_logger.ErrorException("Error saving image size file", ex);
|
||||||
}
|
|
||||||
catch (DirectoryNotFoundException)
|
|
||||||
{
|
|
||||||
// Cache file doesn't exist no biggie
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
|
private string ImageSizeFile
|
||||||
|
{
|
||||||
var parentPath = Path.GetDirectoryName(fullCachePath);
|
get
|
||||||
|
{
|
||||||
Directory.CreateDirectory(parentPath);
|
return Path.Combine(_appPaths.DataPath, "imagesizes.json");
|
||||||
|
|
||||||
// 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 };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,5 +890,10 @@ namespace MediaBrowser.Server.Implementations.Drawing
|
||||||
|
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_saveImageSizeTimer.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -363,6 +363,7 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
{
|
{
|
||||||
if (string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,6 +371,7 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
var parent = Path.GetDirectoryName(i);
|
var parent = Path.GetDirectoryName(i);
|
||||||
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,10 +381,18 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
parent = Path.GetDirectoryName(i);
|
parent = Path.GetDirectoryName(i);
|
||||||
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i.StartsWith(e.FullPath, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
e.FullPath.StartsWith(i, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}))
|
}))
|
||||||
|
@ -390,12 +400,6 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempIgnorePaths.Contains(e.FullPath, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
Logger.Debug("Watcher requested to ignore change to " + e.FullPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
|
Logger.Info("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
|
||||||
|
|
||||||
//Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path
|
//Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.IO;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
@ -8,6 +7,7 @@ using MediaBrowser.Controller.IO;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
|
@ -288,7 +288,7 @@ namespace MediaBrowser.ServerApplication
|
||||||
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager);
|
LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager);
|
||||||
RegisterSingleInstance(LocalizationManager);
|
RegisterSingleInstance(LocalizationManager);
|
||||||
|
|
||||||
ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths, FileSystemManager);
|
ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer);
|
||||||
RegisterSingleInstance(ImageProcessor);
|
RegisterSingleInstance(ImageProcessor);
|
||||||
|
|
||||||
DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
|
DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user