diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-06-28 16:25:58 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-06-28 16:25:58 -0400 |
| commit | 8a1b12b7d805ce35f4e10acf2294f355f47e75e3 (patch) | |
| tree | 71fc6b154b3b4e335fe9af1a58fdb584042dd92a /MediaBrowser.Server.Implementations | |
| parent | ac7d6256f460ce459a5b5c824e1b32d23e120901 (diff) | |
tightened up image saving to reduce knowledge of file names
Diffstat (limited to 'MediaBrowser.Server.Implementations')
3 files changed, 302 insertions, 90 deletions
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 @@ <Compile Include="Persistence\SqliteExtensions.cs" /> <Compile Include="Persistence\TypeMapper.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Providers\ImageSaver.cs" /> <Compile Include="Providers\ProviderManager.cs" /> <Compile Include="ScheduledTasks\ArtistValidationTask.cs" /> <Compile Include="ScheduledTasks\PeopleValidationTask.cs" /> 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 +{ + /// <summary> + /// Class ImageSaver + /// </summary> + public class ImageSaver + { + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + /// <summary> + /// The _config + /// </summary> + private readonly IServerConfigurationManager _config; + + /// <summary> + /// The remote image cache + /// </summary> + private readonly FileSystemRepository _remoteImageCache; + /// <summary> + /// The _directory watchers + /// </summary> + private readonly IDirectoryWatchers _directoryWatchers; + + /// <summary> + /// Initializes a new instance of the <see cref="ImageSaver"/> class. + /// </summary> + /// <param name="config">The config.</param> + /// <param name="directoryWatchers">The directory watchers.</param> + public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers) + { + _config = config; + _directoryWatchers = directoryWatchers; + _remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath); + } + + /// <summary> + /// Saves the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="source">The source.</param> + /// <param name="mimeType">Type of the MIME.</param> + /// <param name="type">The type.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + 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; + } + } + + /// <summary> + /// Gets the save path. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="type">The type.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <param name="mimeType">Type of the MIME.</param> + /// <param name="saveLocally">if set to <c>true</c> [save locally].</param> + /// <returns>System.String.</returns> + /// <exception cref="System.ArgumentNullException"> + /// imageIndex + /// or + /// imageIndex + /// </exception> + 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; @@ -23,11 +26,6 @@ namespace MediaBrowser.Server.Implementations.Providers public class ProviderManager : IProviderManager { /// <summary> - /// The remote image cache - /// </summary> - private readonly FileSystemRepository _remoteImageCache; - - /// <summary> /// The currently running metadata providers /// </summary> private readonly ConcurrentDictionary<string, Tuple<BaseMetadataProvider, BaseItem, CancellationTokenSource>> _currentlyRunningProviders = @@ -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 /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{System.String}.</returns> /// <exception cref="System.ArgumentNullException">item</exception> - public async Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + public Task<string> 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<string> 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; - } - - /// <summary> - /// Gets the save path. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="targetFileName">Name of the target file.</param> - /// <param name="saveLocally">if set to <c>true</c> [save locally].</param> - /// <returns>System.String.</returns> - 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 }; } /// <summary> @@ -462,5 +378,45 @@ namespace MediaBrowser.Server.Implementations.Providers _directoryWatchers.RemoveTempIgnore(path); } } + + + /// <summary> + /// Saves the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="url">The URL.</param> + /// <param name="resourcePool">The resource pool.</param> + /// <param name="type">The type.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + 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); + } + + /// <summary> + /// Saves the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="source">The source.</param> + /// <param name="mimeType">Type of the MIME.</param> + /// <param name="type">The type.</param> + /// <param name="imageIndex">Index of the image.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + 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); + } } } |
