From 8a1b12b7d805ce35f4e10acf2294f355f47e75e3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 28 Jun 2013 16:25:58 -0400 Subject: tightened up image saving to reduce knowledge of file names --- .../MediaBrowser.Server.Implementations.csproj | 1 + .../Providers/ImageSaver.cs | 255 +++++++++++++++++++++ .../Providers/ProviderManager.cs | 136 ++++------- 3 files changed, 302 insertions(+), 90 deletions(-) create mode 100644 MediaBrowser.Server.Implementations/Providers/ImageSaver.cs (limited to 'MediaBrowser.Server.Implementations') diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 08fbe31c0..041c9db6b 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -148,6 +148,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs new file mode 100644 index 000000000..3e5a2802c --- /dev/null +++ b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs @@ -0,0 +1,255 @@ +using System.Globalization; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Entities; +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Providers +{ + /// + /// Class ImageSaver + /// + public class ImageSaver + { + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + /// + /// The _config + /// + private readonly IServerConfigurationManager _config; + + /// + /// The remote image cache + /// + private readonly FileSystemRepository _remoteImageCache; + /// + /// The _directory watchers + /// + private readonly IDirectoryWatchers _directoryWatchers; + + /// + /// Initializes a new instance of the class. + /// + /// The config. + /// The directory watchers. + public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers) + { + _config = config; + _directoryWatchers = directoryWatchers; + _remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath); + } + + /// + /// Saves the image. + /// + /// The item. + /// The source. + /// Type of the MIME. + /// The type. + /// Index of the image. + /// The cancellation token. + /// Task. + public async Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(mimeType)) + { + throw new ArgumentNullException("mimeType"); + } + + var saveLocally = _config.Configuration.SaveLocalMeta; + + if (item is IItemByName) + { + saveLocally = true; + } + else if (item is User) + { + saveLocally = true; + } + else if (item is Audio || item.Parent == null || string.IsNullOrEmpty(item.MetaLocation)) + { + saveLocally = false; + } + + if (type != ImageType.Primary) + { + if (item is Episode) + { + saveLocally = false; + } + } + + if (item.LocationType != LocationType.FileSystem) + { + saveLocally = false; + } + + var path = GetSavePath(item, type, imageIndex, mimeType, saveLocally); + + var currentPath = GetCurrentImagePath(item, type, imageIndex); + + try + { + _directoryWatchers.TemporarilyIgnore(path); + + using (source) + { + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); + } + } + + SetImagePath(item, type, imageIndex, path); + + if (!string.IsNullOrEmpty(currentPath) && !string.Equals(path, currentPath, StringComparison.OrdinalIgnoreCase)) + { + File.Delete(currentPath); + } + } + finally + { + _directoryWatchers.RemoveTempIgnore(path); + } + + } + + private string GetCurrentImagePath(BaseItem item, ImageType type, int? imageIndex) + { + switch (type) + { + case ImageType.Screenshot: + + if (!imageIndex.HasValue) + { + throw new ArgumentNullException("imageIndex"); + } + return item.ScreenshotImagePaths.Count > imageIndex.Value ? item.ScreenshotImagePaths[imageIndex.Value] : null; + case ImageType.Backdrop: + if (!imageIndex.HasValue) + { + throw new ArgumentNullException("imageIndex"); + } + return item.BackdropImagePaths.Count > imageIndex.Value ? item.BackdropImagePaths[imageIndex.Value] : null; + default: + return item.GetImage(type); + } + } + + private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path) + { + switch (type) + { + case ImageType.Screenshot: + + if (!imageIndex.HasValue) + { + throw new ArgumentNullException("imageIndex"); + } + + if (item.ScreenshotImagePaths.Count > imageIndex.Value) + { + item.ScreenshotImagePaths[imageIndex.Value] = path; + } + else + { + item.ScreenshotImagePaths.Add(path); + } + break; + case ImageType.Backdrop: + if (!imageIndex.HasValue) + { + throw new ArgumentNullException("imageIndex"); + } + if (item.BackdropImagePaths.Count > imageIndex.Value) + { + item.BackdropImagePaths[imageIndex.Value] = path; + } + else + { + item.BackdropImagePaths.Add(path); + } + break; + default: + item.SetImage(type, path); + break; + } + } + + /// + /// Gets the save path. + /// + /// The item. + /// The type. + /// Index of the image. + /// Type of the MIME. + /// if set to true [save locally]. + /// System.String. + /// + /// imageIndex + /// or + /// imageIndex + /// + private string GetSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally) + { + string filename; + + switch (type) + { + case ImageType.Art: + filename = "clearart"; + break; + case ImageType.Primary: + filename = item is Episode ? Path.GetFileNameWithoutExtension(item.Path) : "folder"; + break; + case ImageType.Backdrop: + if (!imageIndex.HasValue) + { + throw new ArgumentNullException("imageIndex"); + } + filename = imageIndex.Value == 0 ? "backdrop" : "backdrop" + imageIndex.Value.ToString(UsCulture); + break; + case ImageType.Screenshot: + if (!imageIndex.HasValue) + { + throw new ArgumentNullException("imageIndex"); + } + filename = imageIndex.Value == 0 ? "screenshot" : "screenshot" + imageIndex.Value.ToString(UsCulture); + break; + default: + filename = type.ToString().ToLower(); + break; + } + + var extension = mimeType.Split('/').Last(); + + if (string.Equals(extension, "jpeg", StringComparison.OrdinalIgnoreCase)) + { + extension = "jpg"; + } + + filename += "." + extension.ToLower(); + + var path = (saveLocally && !string.IsNullOrEmpty(item.MetaLocation)) ? + Path.Combine(item.MetaLocation, filename) : + _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Id, filename); + + var parentPath = Path.GetDirectoryName(path); + + if (!Directory.Exists(parentPath)) + { + Directory.CreateDirectory(parentPath); + } + + return path; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index ee8bb4c09..0af7426b1 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -2,11 +2,14 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -22,11 +25,6 @@ namespace MediaBrowser.Server.Implementations.Providers /// public class ProviderManager : IProviderManager { - /// - /// The remote image cache - /// - private readonly FileSystemRepository _remoteImageCache; - /// /// The currently running metadata providers /// @@ -74,7 +72,6 @@ namespace MediaBrowser.Server.Implementations.Providers _httpClient = httpClient; ConfigurationManager = configurationManager; _directoryWatchers = directoryWatchers; - _remoteImageCache = new FileSystemRepository(configurationManager.ApplicationPaths.DownloadedImagesDataPath); configurationManager.ConfigurationUpdated += configurationManager_ConfigurationUpdated; } @@ -206,7 +203,7 @@ namespace MediaBrowser.Server.Implementations.Providers try { var changed = await provider.FetchAsync(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token).ConfigureAwait(false); - + if (changed) { return provider.ItemUpdateType; @@ -315,90 +312,9 @@ namespace MediaBrowser.Server.Implementations.Providers /// The cancellation token. /// Task{System.String}. /// item - public async Task DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + public Task DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally, SemaphoreSlim resourcePool, CancellationToken cancellationToken) { - if (item == null) - { - throw new ArgumentNullException("item"); - } - if (string.IsNullOrEmpty(source)) - { - throw new ArgumentNullException("source"); - } - if (string.IsNullOrEmpty(targetName)) - { - throw new ArgumentNullException("targetName"); - } - if (resourcePool == null) - { - throw new ArgumentNullException("resourcePool"); - } - - var img = await _httpClient.Get(source, resourcePool, cancellationToken).ConfigureAwait(false); - - //download and save locally - return await SaveImage(item, img, targetName, saveLocally, cancellationToken).ConfigureAwait(false); - } - - public async Task SaveImage(BaseItem item, Stream source, string targetName, bool saveLocally, CancellationToken cancellationToken) - { - //download and save locally - var localPath = GetSavePath(item, targetName, saveLocally); - - if (saveLocally) // queue to media directories - { - await SaveToLibraryFilesystem(item, localPath, source, cancellationToken).ConfigureAwait(false); - } - else - { - // we can write directly here because it won't affect the watchers - - try - { - using (var fs = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) - { - await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); - } - } - catch (OperationCanceledException) - { - throw; - } - catch (Exception e) - { - _logger.ErrorException("Error downloading and saving image " + localPath, e); - throw; - } - finally - { - source.Dispose(); - } - - } - return localPath; - } - - /// - /// Gets the save path. - /// - /// The item. - /// Name of the target file. - /// if set to true [save locally]. - /// System.String. - public string GetSavePath(BaseItem item, string targetFileName, bool saveLocally) - { - var path = (saveLocally && item.MetaLocation != null) ? - Path.Combine(item.MetaLocation, targetFileName) : - _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Id.ToString(), targetFileName); - - var parentPath = Path.GetDirectoryName(path); - - if (!Directory.Exists(parentPath)) - { - Directory.CreateDirectory(parentPath); - } - - return path; + throw new HttpException(string.Empty) { IsTimedOut = true }; } /// @@ -462,5 +378,45 @@ namespace MediaBrowser.Server.Implementations.Providers _directoryWatchers.RemoveTempIgnore(path); } } + + + /// + /// Saves the image. + /// + /// The item. + /// The URL. + /// The resource pool. + /// The type. + /// Index of the image. + /// The cancellation token. + /// Task. + public async Task SaveImage(BaseItem item, string url, SemaphoreSlim resourcePool, ImageType type, int? imageIndex, CancellationToken cancellationToken) + { + var response = await _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + ResourcePool = resourcePool, + Url = url + + }).ConfigureAwait(false); + + await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Saves the image. + /// + /// The item. + /// The source. + /// Type of the MIME. + /// The type. + /// Index of the image. + /// The cancellation token. + /// Task. + public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) + { + return new ImageSaver(ConfigurationManager, _directoryWatchers).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); + } } } -- cgit v1.2.3