diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-04-02 22:59:27 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-04-02 22:59:27 -0400 |
| commit | fa884f3fead68522b45276661528ba39d42dcad1 (patch) | |
| tree | 28c77ebff10cae0e72bad5ff08815a9205ef0a67 | |
| parent | 713afcf6755b15142244cad6b16f8d539bd12893 (diff) | |
#62 - File locking problem in cache
16 files changed, 229 insertions, 284 deletions
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 12aac0ccb..a7bcbf821 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Api.Playback.Progressive [Route("/Audio/{Id}/stream.flac", "GET")] [Route("/Audio/{Id}/stream.ogg", "GET")] [Route("/Audio/{Id}/stream.oga", "GET")] - [Route("/Audio/{Id}/stream.webma", "GET")] + [Route("/Audio/{Id}/stream.webm", "GET")] [Route("/Audio/{Id}/stream", "GET")] [Route("/Audio/{Id}/stream.mp3", "HEAD")] [Route("/Audio/{Id}/stream.wma", "HEAD")] @@ -23,7 +23,7 @@ namespace MediaBrowser.Api.Playback.Progressive [Route("/Audio/{Id}/stream.flac", "HEAD")] [Route("/Audio/{Id}/stream.ogg", "HEAD")] [Route("/Audio/{Id}/stream.oga", "HEAD")] - [Route("/Audio/{Id}/stream.webma", "HEAD")] + [Route("/Audio/{Id}/stream.webm", "HEAD")] [Route("/Audio/{Id}/stream", "HEAD")] [Api(Description = "Gets an audio stream")] public class GetAudioStream : StreamRequest diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index f2a9ca24b..a27e3cc32 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -167,11 +167,9 @@ namespace MediaBrowser.Api { var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userManager); - var tasks = _userManager.Users.OrderBy(u => u.Name).Select(dtoBuilder.GetUserDto).ToArray(); + var users = _userManager.Users.OrderBy(u => u.Name).Select(dtoBuilder.GetUserDto).ToArray(); - var task = Task.WhenAll(tasks); - - return ToOptimizedResult(task.Result); + return ToOptimizedResult(users); } /// <summary> @@ -188,7 +186,7 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException("User not found"); } - var result = new DtoBuilder(Logger, _libraryManager, _userManager).GetUserDto(user).Result; + var result = new DtoBuilder(Logger, _libraryManager, _userManager).GetUserDto(user); return ToOptimizedResult(result); } @@ -302,7 +300,7 @@ namespace MediaBrowser.Api newUser.UpdateConfiguration(dtoUser.Configuration, _xmlSerializer); - var result = new DtoBuilder(Logger, _libraryManager, _userManager).GetUserDto(newUser).Result; + var result = new DtoBuilder(Logger, _libraryManager, _userManager).GetUserDto(newUser); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Common/Extensions/NamedLock.cs b/MediaBrowser.Common/Extensions/NamedLock.cs deleted file mode 100644 index d0164c70d..000000000 --- a/MediaBrowser.Common/Extensions/NamedLock.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Extensions -{ - /// <summary> - /// Class NamedLock - /// </summary> - public class NamedLock : IDisposable - { - /// <summary> - /// The _locks - /// </summary> - private readonly Dictionary<string, SemaphoreSlim> _locks = new Dictionary<string, SemaphoreSlim>(); - - /// <summary> - /// Waits the async. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>Task.</returns> - public Task WaitAsync(string name) - { - return GetLock(name).WaitAsync(); - } - - /// <summary> - /// Releases the specified name. - /// </summary> - /// <param name="name">The name.</param> - public void Release(string name) - { - SemaphoreSlim semaphore; - - if (_locks.TryGetValue(name, out semaphore)) - { - semaphore.Release(); - } - } - - /// <summary> - /// Gets the lock. - /// </summary> - /// <param name="filename">The filename.</param> - /// <returns>System.Object.</returns> - private SemaphoreSlim GetLock(string filename) - { - SemaphoreSlim fileLock; - lock (_locks) - { - if (!_locks.TryGetValue(filename, out fileLock)) - { - fileLock = new SemaphoreSlim(1,1); - _locks[filename] = fileLock; - } - } - return fileLock; - } - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - Dispose(true); - } - - /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - DisposeLocks(); - } - } - - /// <summary> - /// Disposes the locks. - /// </summary> - private void DisposeLocks() - { - lock (_locks) - { - foreach (var semaphore in _locks.Values) - { - semaphore.Dispose(); - } - - _locks.Clear(); - } - } - } -} diff --git a/MediaBrowser.Common/IO/FileSystemRepository.cs b/MediaBrowser.Common/IO/FileSystemRepository.cs index 24ad04a92..3a4987a94 100644 --- a/MediaBrowser.Common/IO/FileSystemRepository.cs +++ b/MediaBrowser.Common/IO/FileSystemRepository.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Concurrent; using System.IO; -using System.Threading.Tasks; namespace MediaBrowser.Common.IO { @@ -19,11 +18,6 @@ namespace MediaBrowser.Common.IO private readonly ConcurrentDictionary<string, string> _subFolderPaths = new ConcurrentDictionary<string, string>(); /// <summary> - /// The _file locks - /// </summary> - private readonly NamedLock _fileLocks = new NamedLock(); - - /// <summary> /// Gets or sets the path. /// </summary> /// <value>The path.</value> @@ -171,24 +165,6 @@ namespace MediaBrowser.Common.IO } /// <summary> - /// Waits for lock. - /// </summary> - /// <param name="resourcePath">The resource path.</param> - public Task WaitForLockAsync(string resourcePath) - { - return _fileLocks.WaitAsync(resourcePath); - } - - /// <summary> - /// Releases the lock. - /// </summary> - /// <param name="resourcePath">The resource path.</param> - public void ReleaseLock(string resourcePath) - { - _fileLocks.Release(resourcePath); - } - - /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> public void Dispose() @@ -204,7 +180,6 @@ namespace MediaBrowser.Common.IO { if (dispose) { - _fileLocks.Dispose(); } } } diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index a8fc4564c..e9dda6bb7 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -61,7 +61,6 @@ <Compile Include="Events\EventHelper.cs" /> <Compile Include="Extensions\BaseExtensions.cs" /> <Compile Include="Events\GenericEventArgs.cs" /> - <Compile Include="Extensions\NamedLock.cs" /> <Compile Include="Extensions\ResourceNotFoundException.cs" /> <Compile Include="IO\FileSystemRepository.cs" /> <Compile Include="IO\IIsoManager.cs" /> diff --git a/MediaBrowser.Controller/Drawing/ImageManager.cs b/MediaBrowser.Controller/Drawing/ImageManager.cs index d78ff819b..a5e36da32 100644 --- a/MediaBrowser.Controller/Drawing/ImageManager.cs +++ b/MediaBrowser.Controller/Drawing/ImageManager.cs @@ -15,6 +15,7 @@ using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Drawing @@ -50,7 +51,7 @@ namespace MediaBrowser.Controller.Drawing /// <summary> /// The cached imaged sizes /// </summary> - private readonly ConcurrentDictionary<string, Task<ImageSize>> _cachedImagedSizes = new ConcurrentDictionary<string, Task<ImageSize>>(); + private readonly ConcurrentDictionary<string, ImageSize> _cachedImagedSizes = new ConcurrentDictionary<string, ImageSize>(); /// <summary> /// The _logger @@ -68,11 +69,17 @@ namespace MediaBrowser.Controller.Drawing private readonly Kernel _kernel; /// <summary> + /// The _locks + /// </summary> + private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); + + /// <summary> /// Initializes a new instance of the <see cref="ImageManager" /> class. /// </summary> /// <param name="kernel">The kernel.</param> /// <param name="protobufSerializer">The protobuf serializer.</param> /// <param name="logger">The logger.</param> + /// <param name="appPaths">The app paths.</param> public ImageManager(Kernel kernel, IProtobufSerializer protobufSerializer, ILogger logger, IServerApplicationPaths appPaths) { _protobufSerializer = protobufSerializer; @@ -117,15 +124,7 @@ namespace MediaBrowser.Controller.Drawing if (cropWhitespace) { - try - { - originalImagePath = await GetCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); - } - catch (Exception ex) - { - // We have to have a catch-all here because some of the .net image methods throw a plain old Exception - _logger.ErrorException("Error cropping image", ex); - } + originalImagePath = await GetCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); } try @@ -140,12 +139,12 @@ namespace MediaBrowser.Controller.Drawing originalImagePath = ehnancedImagePath; } } - catch + catch (Exception ex) { - _logger.Error("Error enhancing image"); + _logger.Error("Error enhancing image", ex); } - var originalImageSize = await GetImageSize(originalImagePath, dateModified).ConfigureAwait(false); + var originalImageSize = GetImageSize(originalImagePath, dateModified); // Determine the output size based on incoming parameters var newSize = DrawingUtils.Resize(originalImageSize, width, height, maxWidth, maxHeight); @@ -158,67 +157,102 @@ namespace MediaBrowser.Controller.Drawing var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality.Value, dateModified); // Grab the cache file if it already exists - try + if (File.Exists(cacheFilePath)) { using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) { await fileStream.CopyToAsync(toStream).ConfigureAwait(false); + return; } - return; } - catch (FileNotFoundException) + + var semaphore = GetLock(cacheFilePath); + + await semaphore.WaitAsync().ConfigureAwait(false); + + // Check again in case of lock contention + if (File.Exists(cacheFilePath)) { - // Cache file doesn't exist. No biggie. + try + { + using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + await fileStream.CopyToAsync(toStream).ConfigureAwait(false); + return; + } + } + finally + { + semaphore.Release(); + } } - using (var fileStream = File.OpenRead(originalImagePath)) + try { - using (var originalImage = Bitmap.FromStream(fileStream, true, false)) + using (var fileStream = File.OpenRead(originalImagePath)) { - var newWidth = Convert.ToInt32(newSize.Width); - var newHeight = Convert.ToInt32(newSize.Height); + using (var originalImage = Image.FromStream(fileStream, true, false)) + { + var newWidth = Convert.ToInt32(newSize.Width); + var newHeight = Convert.ToInt32(newSize.Height); - // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here - var thumbnail = !ImageExtensions.IsPixelFormatSupportedByGraphicsObject(originalImage.PixelFormat) ? new Bitmap(originalImage, newWidth, newHeight) : new Bitmap(newWidth, newHeight, originalImage.PixelFormat); + // Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here + var thumbnail = !ImageExtensions.IsPixelFormatSupportedByGraphicsObject(originalImage.PixelFormat) ? new Bitmap(originalImage, newWidth, newHeight) : new Bitmap(newWidth, newHeight, originalImage.PixelFormat); - // Preserve the original resolution - thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution); + // Preserve the original resolution + thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution); - var thumbnailGraph = Graphics.FromImage(thumbnail); + var thumbnailGraph = Graphics.FromImage(thumbnail); - thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality; - thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; - thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; - thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; - thumbnailGraph.CompositingMode = CompositingMode.SourceOver; + thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality; + thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality; + thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic; + thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality; + thumbnailGraph.CompositingMode = CompositingMode.SourceOver; - thumbnailGraph.DrawImage(originalImage, 0, 0, newWidth, newHeight); + thumbnailGraph.DrawImage(originalImage, 0, 0, newWidth, newHeight); - var outputFormat = originalImage.RawFormat; + var outputFormat = originalImage.RawFormat; - using (var memoryStream = new MemoryStream { }) - { - // Save to the memory stream - thumbnail.Save(outputFormat, memoryStream, quality.Value); + using (var memoryStream = new MemoryStream { }) + { + // Save to the memory stream + thumbnail.Save(outputFormat, memoryStream, quality.Value); - var bytes = memoryStream.ToArray(); + var bytes = memoryStream.ToArray(); - var outputTask = Task.Run(async () => await toStream.WriteAsync(bytes, 0, bytes.Length)); + var outputTask = toStream.WriteAsync(bytes, 0, bytes.Length); - // Save to the cache location - using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) - { - // Save to the filestream - await cacheFileStream.WriteAsync(bytes, 0, bytes.Length); + // kick off a task to cache the result + Task.Run(() => CacheResizedImage(cacheFilePath, bytes)); + + await outputTask.ConfigureAwait(false); } - await outputTask.ConfigureAwait(false); + thumbnailGraph.Dispose(); + thumbnail.Dispose(); } - - thumbnailGraph.Dispose(); - thumbnail.Dispose(); } } + finally + { + semaphore.Release(); + } + } + + /// <summary> + /// Caches the resized image. + /// </summary> + /// <param name="cacheFilePath">The cache file path.</param> + /// <param name="bytes">The bytes.</param> + private async void CacheResizedImage(string cacheFilePath, byte[] bytes) + { + // Save to the cache location + using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + // Save to the filestream + await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); + } } /// <summary> @@ -252,7 +286,7 @@ namespace MediaBrowser.Controller.Drawing /// <param name="dateModified">The date modified.</param> /// <returns>Task{ImageSize}.</returns> /// <exception cref="System.ArgumentNullException">imagePath</exception> - public Task<ImageSize> GetImageSize(string imagePath, DateTime dateModified) + public ImageSize GetImageSize(string imagePath, DateTime dateModified) { if (string.IsNullOrEmpty(imagePath)) { @@ -261,18 +295,7 @@ namespace MediaBrowser.Controller.Drawing var name = imagePath + "datemodified=" + dateModified.Ticks; - return _cachedImagedSizes.GetOrAdd(name, keyName => GetImageSizeTask(keyName, imagePath)); - } - - /// <summary> - /// Gets cached image dimensions, or results null if non-existant - /// </summary> - /// <param name="keyName">Name of the key.</param> - /// <param name="imagePath">The image path.</param> - /// <returns>Task{ImageSize}.</returns> - private Task<ImageSize> GetImageSizeTask(string keyName, string imagePath) - { - return Task.Run(() => GetImageSize(keyName, imagePath)); + return _cachedImagedSizes.GetOrAdd(name, keyName => GetImageSize(keyName, imagePath)); } /// <summary> @@ -297,27 +320,14 @@ namespace MediaBrowser.Controller.Drawing // Cache file doesn't exist no biggie } - var size = ImageHeader.GetDimensions(imagePath, _logger); + _logger.Debug("Getting image size for {0}", imagePath); - var imageSize = new ImageSize { Width = size.Width, Height = size.Height }; + var size = ImageHeader.GetDimensions(imagePath, _logger); // Update the file system cache - CacheImageSize(fullCachePath, size.Width, size.Height); - - return imageSize; - } + Task.Run(() => _protobufSerializer.SerializeToFile(new[] { size.Width, size.Height }, fullCachePath)); - /// <summary> - /// Caches image dimensions - /// </summary> - /// <param name="cachePath">The cache path.</param> - /// <param name="width">The width.</param> - /// <param name="height">The height.</param> - private void CacheImageSize(string cachePath, int width, int height) - { - var output = new[] { width, height }; - - _protobufSerializer.SerializeToFile(output, cachePath); + return new ImageSize { Width = size.Width, Height = size.Height }; } /// <summary> @@ -367,7 +377,7 @@ namespace MediaBrowser.Controller.Drawing return video.Chapters[imageIndex].ImagePath; } - + return item.GetImage(imageType); } @@ -409,7 +419,7 @@ namespace MediaBrowser.Controller.Drawing { throw new ArgumentNullException("imagePath"); } - + var metaFileEntry = item.ResolveArgs.GetMetaFileByPath(imagePath); // If we didn't the metafile entry, check the Season @@ -440,38 +450,53 @@ namespace MediaBrowser.Controller.Drawing var croppedImagePath = CroppedImageCache.GetResourcePath(name, Path.GetExtension(originalImagePath)); - if (!CroppedImageCache.ContainsFilePath(croppedImagePath)) + if (CroppedImageCache.ContainsFilePath(croppedImagePath)) + { + return croppedImagePath; + } + + var semaphore = GetLock(croppedImagePath); + + await semaphore.WaitAsync().ConfigureAwait(false); + + // Check again in case of contention + if (CroppedImageCache.ContainsFilePath(croppedImagePath)) + { + semaphore.Release(); + return croppedImagePath; + } + + try { using (var fileStream = File.OpenRead(originalImagePath)) { - using (var originalImage = (Bitmap)Bitmap.FromStream(fileStream, true, false)) + using (var originalImage = (Bitmap)Image.FromStream(fileStream, true, false)) { var outputFormat = originalImage.RawFormat; using (var croppedImage = originalImage.CropWhitespace()) { - await SaveImageToFile(croppedImage, outputFormat, croppedImagePath).ConfigureAwait(false); + using (var outputStream = new FileStream(croppedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + croppedImage.Save(outputFormat, outputStream, 100); + } } } } } - - return croppedImagePath; - } - - private async Task SaveImageToFile(Image image, ImageFormat outputFormat, string file) - { - using (var memoryStream = new MemoryStream()) + catch (Exception ex) { - image.Save(outputFormat, memoryStream, 100); + // We have to have a catch-all here because some of the .net image methods throw a plain old Exception + _logger.ErrorException("Error cropping image {0}", ex, originalImagePath); - memoryStream.Position = 0; - - using (var cacheFileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) - { - await memoryStream.CopyToAsync(cacheFileStream).ConfigureAwait(false); - } + return originalImagePath; + } + finally + { + semaphore.Release(); } + + return croppedImagePath; } /// <summary> @@ -509,7 +534,23 @@ namespace MediaBrowser.Controller.Drawing // All enhanced images are saved as png to allow transparency var enhancedImagePath = EnhancedImageCache.GetResourcePath(cacheGuid + ".png"); - if (!EnhancedImageCache.ContainsFilePath(enhancedImagePath)) + if (EnhancedImageCache.ContainsFilePath(enhancedImagePath)) + { + return enhancedImagePath; + } + + var semaphore = GetLock(enhancedImagePath); + + await semaphore.WaitAsync().ConfigureAwait(false); + + // Check again in case of contention + if (EnhancedImageCache.ContainsFilePath(enhancedImagePath)) + { + semaphore.Release(); + return enhancedImagePath; + } + + try { using (var fileStream = File.OpenRead(originalImagePath)) { @@ -519,11 +560,18 @@ namespace MediaBrowser.Controller.Drawing using (var newImage = await ExecuteImageEnhancers(supportedEnhancers, originalImage, item, imageType, imageIndex).ConfigureAwait(false)) { //And then save it in the cache - await SaveImageToFile(newImage, ImageFormat.Png, enhancedImagePath).ConfigureAwait(false); + using (var outputStream = new FileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + newImage.Save(ImageFormat.Png, outputStream, 100); + } } } } } + finally + { + semaphore.Release(); + } return enhancedImagePath; } @@ -547,7 +595,7 @@ namespace MediaBrowser.Controller.Drawing { throw new ArgumentNullException("imagePath"); } - + var dateModified = GetImageDateModified(item, imagePath); var supportedEnhancers = _kernel.ImageEnhancers.Where(i => i.Supports(item, imageType)); @@ -624,6 +672,19 @@ namespace MediaBrowser.Controller.Drawing return result; } + /// <summary> + /// Gets the lock. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>System.Object.</returns> + private SemaphoreSlim GetLock(string filename) + { + return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> public void Dispose() { Dispose(true); diff --git a/MediaBrowser.Controller/Library/DtoBuilder.cs b/MediaBrowser.Controller/Library/DtoBuilder.cs index d9244c64c..e87282c1f 100644 --- a/MediaBrowser.Controller/Library/DtoBuilder.cs +++ b/MediaBrowser.Controller/Library/DtoBuilder.cs @@ -59,11 +59,21 @@ namespace MediaBrowser.Controller.Library var tasks = new List<Task>(); + if (fields.Contains(ItemFields.Studios)) + { + dto.Studios = item.Studios; + } + + if (fields.Contains(ItemFields.People)) + { + tasks.Add(AttachPeople(dto, item)); + } + if (fields.Contains(ItemFields.PrimaryImageAspectRatio)) { try { - tasks.Add(AttachPrimaryImageAspectRatio(dto, item)); + AttachPrimaryImageAspectRatio(dto, item); } catch (Exception ex) { @@ -72,16 +82,6 @@ namespace MediaBrowser.Controller.Library } } - if (fields.Contains(ItemFields.Studios)) - { - dto.Studios = item.Studios; - } - - if (fields.Contains(ItemFields.People)) - { - tasks.Add(AttachPeople(dto, item)); - } - AttachBasicFields(dto, item, fields); // Make sure all the tasks we kicked off have completed. @@ -120,19 +120,6 @@ namespace MediaBrowser.Controller.Library var tasks = new List<Task>(); - if (fields.Contains(ItemFields.PrimaryImageAspectRatio)) - { - try - { - tasks.Add(AttachPrimaryImageAspectRatio(dto, item)); - } - catch (Exception ex) - { - // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions - _logger.ErrorException("Error generating PrimaryImageAspectRatio for {0}", ex, item.Name); - } - } - if (fields.Contains(ItemFields.Studios)) { dto.Studios = item.Studios; @@ -145,6 +132,19 @@ namespace MediaBrowser.Controller.Library tasks.Add(AttachUserSpecificInfo(dto, item, user, fields)); + if (fields.Contains(ItemFields.PrimaryImageAspectRatio)) + { + try + { + AttachPrimaryImageAspectRatio(dto, item); + } + catch (Exception ex) + { + // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions + _logger.ErrorException("Error generating PrimaryImageAspectRatio for {0}", ex, item.Name); + } + } + AttachBasicFields(dto, item, fields); // Make sure all the tasks we kicked off have completed. @@ -199,7 +199,7 @@ namespace MediaBrowser.Controller.Library /// <param name="dto">The dto.</param> /// <param name="item">The item.</param> /// <returns>Task.</returns> - private async Task AttachPrimaryImageAspectRatio(IItemDto dto, BaseItem item) + private void AttachPrimaryImageAspectRatio(IItemDto dto, BaseItem item) { var path = item.PrimaryImagePath; @@ -217,7 +217,7 @@ namespace MediaBrowser.Controller.Library try { - size = await Kernel.Instance.ImageManager.GetImageSize(path, dateModified).ConfigureAwait(false); + size = Kernel.Instance.ImageManager.GetImageSize(path, dateModified); } catch (FileNotFoundException) { @@ -771,7 +771,7 @@ namespace MediaBrowser.Controller.Library /// <param name="user">The user.</param> /// <returns>DtoUser.</returns> /// <exception cref="System.ArgumentNullException">user</exception> - public async Task<UserDto> GetUserDto(User user) + public UserDto GetUserDto(User user) { if (user == null) { @@ -796,7 +796,7 @@ namespace MediaBrowser.Controller.Library try { - await AttachPrimaryImageAspectRatio(dto, user).ConfigureAwait(false); + AttachPrimaryImageAspectRatio(dto, user); } catch (Exception ex) { diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index ca3c1fe7c..c5e4de2bc 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -300,10 +300,18 @@ namespace MediaBrowser.Server.Implementations.Library var activityDate = DateTime.UtcNow; + var lastActivityDate = user.LastActivityDate; + user.LastActivityDate = activityDate; LogConnection(user.Id, clientType, deviceId, deviceName, activityDate); + // Don't log in the db anymore frequently than 10 seconds + if (lastActivityDate.HasValue && (activityDate - lastActivityDate.Value).TotalSeconds < 10) + { + return Task.FromResult(true); + } + // Save this directly. No need to fire off all the events for this. return Kernel.UserRepository.SaveUser(user, CancellationToken.None); } diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs index 44674e125..8a15d4028 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs @@ -48,10 +48,10 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="protobufSerializer">The protobuf serializer.</param> - /// <param name="logger">The logger.</param> + /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">protobufSerializer</exception> - public SQLiteDisplayPreferencesRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogger logger) - : base(logger) + public SQLiteDisplayPreferencesRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogManager logManager) + : base(logManager) { if (protobufSerializer == null) { diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs index 6a9a9f6f5..3403cf79f 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs @@ -56,10 +56,10 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="logger">The logger.</param> + /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">appPaths</exception> - public SQLiteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogger logger) - : base(logger) + public SQLiteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) + : base(logManager) { if (appPaths == null) { diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs index 1173f3fc8..c5320a1f6 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs @@ -46,16 +46,16 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// <summary> /// Initializes a new instance of the <see cref="SqliteRepository" /> class. /// </summary> - /// <param name="logger">The logger.</param> + /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">logger</exception> - protected SqliteRepository(ILogger logger) + protected SqliteRepository(ILogManager logManager) { - if (logger == null) + if (logManager == null) { - throw new ArgumentNullException("logger"); + throw new ArgumentNullException("logManager"); } - Logger = logger; + Logger = logManager.GetLogger(GetType().Name); } /// <summary> diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs index f3dede889..2c8d7f437 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs @@ -49,10 +49,10 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="protobufSerializer">The protobuf serializer.</param> - /// <param name="logger">The logger.</param> + /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">protobufSerializer</exception> - public SQLiteUserDataRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogger logger) - : base(logger) + public SQLiteUserDataRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogManager logManager) + : base(logManager) { if (protobufSerializer == null) { diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs index 43dffc596..812c98789 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs @@ -50,10 +50,10 @@ namespace MediaBrowser.Server.Implementations.Sqlite /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="logger">The logger.</param> + /// <param name="logManager">The log manager.</param> /// <exception cref="System.ArgumentNullException">appPaths</exception> - public SQLiteUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogger logger) - : base(logger) + public SQLiteUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) + : base(logManager) { if (appPaths == null) { diff --git a/MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs b/MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs index 9ddd14491..98d7fb477 100644 --- a/MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs +++ b/MediaBrowser.ServerApplication/EntryPoints/WebSocketEvents.cs @@ -172,9 +172,9 @@ namespace MediaBrowser.ServerApplication.EntryPoints /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The e.</param> - async void userManager_UserUpdated(object sender, GenericEventArgs<User> e) + void userManager_UserUpdated(object sender, GenericEventArgs<User> e) { - var dto = await new DtoBuilder(_logger, _libraryManager, _userManager).GetUserDto(e.Argument).ConfigureAwait(false); + var dto = new DtoBuilder(_logger, _libraryManager, _userManager).GetUserDto(e.Argument); _serverManager.SendWebSocketMessage("UserUpdated", dto); } diff --git a/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs b/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs index 6109f73bb..debe78599 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardInfoWebSocketListener.cs @@ -40,9 +40,11 @@ namespace MediaBrowser.WebDashboard.Api /// <summary> /// Initializes a new instance of the <see cref="DashboardInfoWebSocketListener" /> class. /// </summary> + /// <param name="appHost">The app host.</param> /// <param name="logger">The logger.</param> /// <param name="taskManager">The task manager.</param> /// <param name="userManager">The user manager.</param> + /// <param name="libraryManager">The library manager.</param> public DashboardInfoWebSocketListener(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, ILibraryManager libraryManager) : base(logger) { @@ -59,7 +61,7 @@ namespace MediaBrowser.WebDashboard.Api /// <returns>Task{IEnumerable{TaskInfo}}.</returns> protected override Task<DashboardInfo> GetDataToSend(object state) { - return DashboardService.GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager); + return Task.FromResult(DashboardService.GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager)); } } } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index f6c64b49e..15ccbfb3e 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -147,7 +147,7 @@ namespace MediaBrowser.WebDashboard.Api /// <returns>System.Object.</returns> public object Get(GetDashboardInfo request) { - return GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager).Result; + return GetDashboardInfo(_appHost, Logger, _taskManager, _userManager, _libraryManager); } /// <summary> @@ -159,14 +159,13 @@ namespace MediaBrowser.WebDashboard.Api /// <param name="userManager">The user manager.</param> /// <param name="libraryManager">The library manager.</param> /// <returns>DashboardInfo.</returns> - public static async Task<DashboardInfo> GetDashboardInfo(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, ILibraryManager libraryManager) + public static DashboardInfo GetDashboardInfo(IServerApplicationHost appHost, ILogger logger, ITaskManager taskManager, IUserManager userManager, ILibraryManager libraryManager) { var connections = userManager.RecentConnections.ToArray(); var dtoBuilder = new DtoBuilder(logger, libraryManager, userManager); - var tasks = userManager.Users.Where(u => connections.Any(c => c.UserId == u.Id)).Select(dtoBuilder.GetUserDto); - var users = await Task.WhenAll(tasks).ConfigureAwait(false); + var users = userManager.Users.Where(u => connections.Any(c => c.UserId == u.Id)).Select(dtoBuilder.GetUserDto); return new DashboardInfo { @@ -180,7 +179,7 @@ namespace MediaBrowser.WebDashboard.Api ActiveConnections = connections, - Users = users + Users = users.ToArray() }; } |
