tightened up image saving to reduce knowledge of file names
This commit is contained in:
parent
ac7d6256f4
commit
8a1b12b7d8
|
@ -760,145 +760,45 @@ namespace MediaBrowser.Api.Images
|
|||
var bytes = Convert.FromBase64String(text);
|
||||
|
||||
// Validate first
|
||||
using (var memoryStream = new MemoryStream(bytes))
|
||||
using (var validationStream = new MemoryStream(bytes))
|
||||
{
|
||||
using (var image = Image.FromStream(memoryStream))
|
||||
using (var image = Image.FromStream(validationStream))
|
||||
{
|
||||
Logger.Info("New image is {0}x{1}", image.Width, image.Height);
|
||||
}
|
||||
}
|
||||
|
||||
string filename;
|
||||
var memoryStream = new MemoryStream(bytes);
|
||||
|
||||
switch (imageType)
|
||||
{
|
||||
case ImageType.Art:
|
||||
filename = "clearart";
|
||||
break;
|
||||
case ImageType.Primary:
|
||||
filename = entity is Episode ? Path.GetFileNameWithoutExtension(entity.Path) : "folder";
|
||||
break;
|
||||
case ImageType.Backdrop:
|
||||
filename = GetBackdropFilenameToSave(entity);
|
||||
break;
|
||||
case ImageType.Screenshot:
|
||||
filename = GetScreenshotFilenameToSave(entity);
|
||||
break;
|
||||
default:
|
||||
filename = imageType.ToString().ToLower();
|
||||
break;
|
||||
}
|
||||
memoryStream.Position = 0;
|
||||
|
||||
|
||||
var extension = mimeType.Split(';').First().Split('/').Last();
|
||||
|
||||
string oldImagePath;
|
||||
switch (imageType)
|
||||
{
|
||||
case ImageType.Backdrop:
|
||||
case ImageType.Screenshot:
|
||||
oldImagePath = null;
|
||||
break;
|
||||
default:
|
||||
oldImagePath = entity.GetImage(imageType);
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't save locally if there's no parent (special feature, trailer, etc)
|
||||
var saveLocally = !(entity is Audio) && entity.Parent != null && !string.IsNullOrEmpty(entity.MetaLocation) || entity is User;
|
||||
|
||||
if (imageType != ImageType.Primary)
|
||||
{
|
||||
if (entity is Episode)
|
||||
{
|
||||
saveLocally = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.LocationType != LocationType.FileSystem)
|
||||
{
|
||||
saveLocally = false;
|
||||
}
|
||||
|
||||
var imagePath = _providerManager.GetSavePath(entity, filename + "." + extension, saveLocally);
|
||||
|
||||
// Save to file system
|
||||
using (var fs = new FileStream(imagePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
|
||||
{
|
||||
await fs.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
}
|
||||
var imageIndex = 0;
|
||||
|
||||
if (imageType == ImageType.Screenshot)
|
||||
{
|
||||
entity.ScreenshotImagePaths.Add(imagePath);
|
||||
imageIndex = entity.ScreenshotImagePaths.Count;
|
||||
}
|
||||
else if (imageType == ImageType.Backdrop)
|
||||
{
|
||||
entity.BackdropImagePaths.Add(imagePath);
|
||||
imageIndex = entity.BackdropImagePaths.Count;
|
||||
}
|
||||
|
||||
await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, imageIndex, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var user = entity as User;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
await _userManager.UpdateUser(user).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the image
|
||||
entity.SetImage(imageType, imagePath);
|
||||
await _libraryManager.UpdateItem(entity, ItemUpdateType.ImageUpdate, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// If the new and old paths are different, delete the old one
|
||||
if (!string.IsNullOrEmpty(oldImagePath) && !oldImagePath.Equals(imagePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Delete(oldImagePath);
|
||||
}
|
||||
|
||||
// Directory watchers should repeat this, but do a quick refresh first
|
||||
await entity.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false).ConfigureAwait(false);
|
||||
await entity.RefreshMetadata(CancellationToken.None, allowSlowProviders: false).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the backdrop filename to save.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetBackdropFilenameToSave(BaseItem item)
|
||||
{
|
||||
var paths = item.BackdropImagePaths.ToList();
|
||||
|
||||
if (!paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "backdrop", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return "screenshot";
|
||||
}
|
||||
|
||||
var index = 1;
|
||||
|
||||
while (paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "backdrop" + index, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
return "backdrop" + index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screenshot filename to save.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetScreenshotFilenameToSave(BaseItem item)
|
||||
{
|
||||
var paths = item.ScreenshotImagePaths.ToList();
|
||||
|
||||
if (!paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "screenshot", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return "screenshot";
|
||||
}
|
||||
|
||||
var index = 1;
|
||||
|
||||
while (paths.Any(i => string.Equals(Path.GetFileNameWithoutExtension(i), "screenshot" + index, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
return "screenshot" + index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,6 +104,92 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
|
|||
return client;
|
||||
}
|
||||
|
||||
public async Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
|
||||
{
|
||||
ValidateParams(options.Url, options.CancellationToken);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
|
||||
|
||||
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30)
|
||||
{
|
||||
throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url)) { IsTimedOut = true };
|
||||
}
|
||||
|
||||
using (var message = GetHttpRequestMessage(options))
|
||||
{
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < 30)
|
||||
{
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
options.ResourcePool.Release();
|
||||
}
|
||||
|
||||
throw new HttpException(string.Format("Connection to {0} timed out", options.Url)) { IsTimedOut = true };
|
||||
}
|
||||
|
||||
_logger.Info("HttpClientManager.Get url: {0}", options.Url);
|
||||
|
||||
try
|
||||
{
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var response = await client.HttpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead, options.CancellationToken).ConfigureAwait(false);
|
||||
|
||||
EnsureSuccessStatusCode(response);
|
||||
|
||||
options.CancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
return new HttpResponseInfo
|
||||
{
|
||||
Content = await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
|
||||
StatusCode = response.StatusCode,
|
||||
|
||||
ContentType = response.Content.Headers.ContentType.MediaType
|
||||
};
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
var exception = GetCancellationException(options.Url, options.CancellationToken, ex);
|
||||
|
||||
var httpException = exception as HttpException;
|
||||
|
||||
if (httpException != null && httpException.IsTimedOut)
|
||||
{
|
||||
client.LastTimeout = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
throw exception;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
||||
|
||||
throw new HttpException(ex.Message, ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting response from " + options.Url, ex);
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (options.ResourcePool != null)
|
||||
{
|
||||
options.ResourcePool.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a GET request and returns the resulting stream
|
||||
/// </summary>
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MediaBrowser.Common.Implementations.HttpClientManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpResponseOutput
|
||||
/// </summary>
|
||||
public class HttpResponseInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the URL.
|
||||
/// </summary>
|
||||
/// <value>The URL.</value>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the etag.
|
||||
/// </summary>
|
||||
/// <value>The etag.</value>
|
||||
public string Etag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last modified.
|
||||
/// </summary>
|
||||
/// <value>The last modified.</value>
|
||||
public DateTime? LastModified { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expires.
|
||||
/// </summary>
|
||||
/// <value>The expires.</value>
|
||||
public DateTime? Expires { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [must revalidate].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [must revalidate]; otherwise, <c>false</c>.</value>
|
||||
public bool MustRevalidate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request date.
|
||||
/// </summary>
|
||||
/// <value>The request date.</value>
|
||||
public DateTime RequestDate { get; set; }
|
||||
}
|
||||
}
|
|
@ -66,7 +66,6 @@
|
|||
<Compile Include="Configuration\BaseConfigurationManager.cs" />
|
||||
<Compile Include="HttpClientManager\HttpClientInfo.cs" />
|
||||
<Compile Include="HttpClientManager\HttpClientManager.cs" />
|
||||
<Compile Include="HttpClientManager\HttpResponseInfo.cs" />
|
||||
<Compile Include="Logging\LogHelper.cs" />
|
||||
<Compile Include="Logging\NLogger.cs" />
|
||||
<Compile Include="Logging\NlogManager.cs" />
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
<Compile Include="Net\BasePeriodicWebSocketListener.cs" />
|
||||
<Compile Include="Configuration\IApplicationPaths.cs" />
|
||||
<Compile Include="Net\HttpRequestOptions.cs" />
|
||||
<Compile Include="Net\HttpResponseInfo.cs" />
|
||||
<Compile Include="Net\IHasResultFactory.cs" />
|
||||
<Compile Include="Net\IHttpResultFactory.cs" />
|
||||
<Compile Include="Net\IServerManager.cs" />
|
||||
|
|
29
MediaBrowser.Common/Net/HttpResponseInfo.cs
Normal file
29
MediaBrowser.Common/Net/HttpResponseInfo.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Common.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HttpResponseInfo
|
||||
/// </summary>
|
||||
public class HttpResponseInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the content.
|
||||
/// </summary>
|
||||
/// <value>The type of the content.</value>
|
||||
public string ContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content.
|
||||
/// </summary>
|
||||
/// <value>The content.</value>
|
||||
public Stream Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
/// <value>The status code.</value>
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
}
|
||||
}
|
|
@ -11,6 +11,13 @@ namespace MediaBrowser.Common.Net
|
|||
/// </summary>
|
||||
public interface IHttpClient : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the response.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task{HttpResponseInfo}.</returns>
|
||||
Task<HttpResponseInfo> GetResponse(HttpRequestOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a GET request and returns the resulting stream
|
||||
/// </summary>
|
||||
|
|
|
@ -798,6 +798,9 @@ namespace MediaBrowser.Controller.Entities
|
|||
});
|
||||
|
||||
await ((Folder)child).ValidateChildren(innerProgress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
|
||||
|
||||
// Some folder providers are unable to refresh until children have been refreshed.
|
||||
await child.RefreshMetadata(cancellationToken, resetResolveArgs: false).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
@ -12,40 +13,8 @@ namespace MediaBrowser.Controller.Providers
|
|||
/// </summary>
|
||||
public interface IProviderManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Downloads the and save image.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="targetName">Name of the target.</param>
|
||||
/// <param name="saveLocally">if set to <c>true</c> [save locally].</param>
|
||||
/// <param name="resourcePool">The resource pool.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{System.String}.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">item</exception>
|
||||
Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally, SemaphoreSlim resourcePool, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the image.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="targetName">Name of the target.</param>
|
||||
/// <param name="saveLocally">if set to <c>true</c> [save locally].</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{System.String}.</returns>
|
||||
Task<string> SaveImage(BaseItem item, Stream source, string targetName, bool saveLocally, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Saves to library filesystem.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="dataToSave">The data to save.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
Task SaveToLibraryFilesystem(BaseItem item, string path, Stream dataToSave, CancellationToken cancellationToken);
|
||||
Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, bool saveLocally,
|
||||
SemaphoreSlim resourcePool, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the metadata providers.
|
||||
|
@ -57,19 +26,34 @@ namespace MediaBrowser.Controller.Providers
|
|||
/// <returns>Task{System.Boolean}.</returns>
|
||||
Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true);
|
||||
|
||||
/// <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>
|
||||
Task SaveImage(BaseItem item, string url, SemaphoreSlim resourcePool, ImageType type, int? imageIndex, CancellationToken cancellationToken);
|
||||
|
||||
/// <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>
|
||||
Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the metadata providers.
|
||||
/// </summary>
|
||||
/// <param name="providers">The providers.</param>
|
||||
void AddParts(IEnumerable<BaseMetadataProvider> providers);
|
||||
|
||||
/// <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>
|
||||
string GetSavePath(BaseItem item, string targetFileName, bool saveLocally);
|
||||
}
|
||||
}
|
|
@ -303,9 +303,6 @@ namespace MediaBrowser.Providers.Movies
|
|||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var saveLocal = ConfigurationManager.Configuration.SaveLocalMeta &&
|
||||
item.LocationType == LocationType.FileSystem;
|
||||
|
||||
string path;
|
||||
var hd = ConfigurationManager.Configuration.DownloadHDFanArt ? "hd" : "";
|
||||
|
||||
|
@ -322,7 +319,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LogoFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Logo, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -337,7 +334,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ArtFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Art, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -349,7 +347,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(item, path, DiscFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Disc, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,7 +361,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BannerFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Banner, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,7 +375,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(item, path, ThumbFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Thumb, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,7 +394,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("backdrop" + (numBackdrops > 0 ? numBackdrops.ToString(UsCulture) : "") + ".jpg"), saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Backdrop, numBackdrops, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
numBackdrops++;
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
return ItemUpdateType.ImageUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [requires internet].
|
||||
/// </summary>
|
||||
|
@ -148,7 +148,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return base.NeedsRefreshInternal(item, providerInfo);
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
data = new BaseProviderInfo();
|
||||
item.ProviderData[Id] = data;
|
||||
}
|
||||
|
||||
|
||||
var images = await FetchImages(item, item.GetProviderId(MetadataProviders.Tmdb), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var status = await ProcessImages(item, images, cancellationToken).ConfigureAwait(false);
|
||||
|
@ -246,7 +246,9 @@ namespace MediaBrowser.Providers.Movies
|
|||
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
item.PrimaryImagePath = await _providerManager.SaveImage(item, img, "folder" + Path.GetExtension(poster.file_path), ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem, cancellationToken).ConfigureAwait(false);
|
||||
await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(poster.file_path), ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,7 +276,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
item.BackdropImagePaths.Add(await _providerManager.SaveImage(item, img, bdName + Path.GetExtension(images.backdrops[i].file_path), ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(images.backdrops[i].file_path), ImageType.Backdrop, item.BackdropImagePaths.Count, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (item.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops)
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
/// <summary>
|
||||
/// The movie db
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim _movieDbResourcePool = new SemaphoreSlim(1,1);
|
||||
private readonly SemaphoreSlim _movieDbResourcePool = new SemaphoreSlim(1, 1);
|
||||
|
||||
internal static MovieDbProvider Current { get; private set; }
|
||||
|
||||
|
@ -158,7 +158,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
_tmdbSettingsSemaphore.Release();
|
||||
return _tmdbSettings;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
using (var json = await GetMovieDbResponse(new HttpRequestOptions
|
||||
|
@ -199,7 +199,13 @@ namespace MediaBrowser.Providers.Movies
|
|||
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
|
||||
{
|
||||
if (HasAltMeta(item))
|
||||
return false;
|
||||
return false;
|
||||
|
||||
// Boxsets require two passes because we need the children to be refreshed
|
||||
if (item is BoxSet && string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.TmdbCollection)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.NeedsRefreshInternal(item, providerInfo);
|
||||
}
|
||||
|
@ -291,19 +297,6 @@ namespace MediaBrowser.Providers.Movies
|
|||
/// <returns>Task{System.String}.</returns>
|
||||
public async Task<string> FindId(BaseItem item, int? productionYear, CancellationToken cancellationToken)
|
||||
{
|
||||
string id = null;
|
||||
|
||||
if (item.LocationType == LocationType.FileSystem)
|
||||
{
|
||||
string justName = item.Path != null ? item.Path.Substring(item.Path.LastIndexOf(Path.DirectorySeparatorChar)) : string.Empty;
|
||||
id = justName.GetAttributeValue("tmdbid");
|
||||
if (id != null)
|
||||
{
|
||||
Logger.Debug("Using tmdb id specified in path.");
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
int? year;
|
||||
string name = item.Name;
|
||||
ParseName(name, out name, out year);
|
||||
|
@ -320,25 +313,14 @@ namespace MediaBrowser.Providers.Movies
|
|||
var boxset = item as BoxSet;
|
||||
if (boxset != null)
|
||||
{
|
||||
var firstChild = boxset.Children.FirstOrDefault();
|
||||
|
||||
if (firstChild != null)
|
||||
{
|
||||
Logger.Debug("MovieDbProvider - Attempting to find boxset ID from: " + firstChild.Name);
|
||||
string childName;
|
||||
int? childYear;
|
||||
ParseName(firstChild.Name, out childName, out childYear);
|
||||
id = await GetBoxsetIdFromMovie(childName, childYear, language, cancellationToken).ConfigureAwait(false);
|
||||
if (id != null)
|
||||
{
|
||||
Logger.Info("MovieDbProvider - Found Boxset ID: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
// See if any movies have a collection id already
|
||||
return boxset.Children.OfType<Video>()
|
||||
.Select(i => i.GetProviderId(MetadataProviders.TmdbCollection))
|
||||
.FirstOrDefault(i => i != null);
|
||||
}
|
||||
|
||||
//nope - search for it
|
||||
id = await AttemptFindId(name, year, language, cancellationToken).ConfigureAwait(false);
|
||||
var id = await AttemptFindId(name, year, language, cancellationToken).ConfigureAwait(false);
|
||||
if (id == null)
|
||||
{
|
||||
//try in english if wasn't before
|
||||
|
@ -509,7 +491,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
DateTime r;
|
||||
|
||||
//These dates are always in this exact format
|
||||
if (DateTime.TryParseExact(possible.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r))
|
||||
if (DateTime.TryParseExact(possible.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r))
|
||||
{
|
||||
if (Math.Abs(r.Year - year.Value) > 1) // allow a 1 year tolerance on release date
|
||||
{
|
||||
|
@ -708,7 +690,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
var ourRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals(ConfigurationManager.Configuration.MetadataCountryCode, StringComparison.OrdinalIgnoreCase)) ?? new Country();
|
||||
var usRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase)) ?? new Country();
|
||||
var minimunRelease = movieData.releases.countries.OrderBy(c => c.release_date).FirstOrDefault() ?? new Country();
|
||||
var ratingPrefix = ConfigurationManager.Configuration.MetadataCountryCode.Equals("us", StringComparison.OrdinalIgnoreCase) ? "" : ConfigurationManager.Configuration.MetadataCountryCode +"-";
|
||||
var ratingPrefix = ConfigurationManager.Configuration.MetadataCountryCode.Equals("us", StringComparison.OrdinalIgnoreCase) ? "" : ConfigurationManager.Configuration.MetadataCountryCode + "-";
|
||||
movie.OfficialRating = !string.IsNullOrEmpty(ourRelease.certification)
|
||||
? ratingPrefix + ourRelease.certification
|
||||
: !string.IsNullOrEmpty(usRelease.certification)
|
||||
|
@ -725,7 +707,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
movie.ProductionYear = ourRelease.release_date.Year;
|
||||
}
|
||||
}
|
||||
else if(usRelease.release_date != default (DateTime))
|
||||
else if (usRelease.release_date != default(DateTime))
|
||||
{
|
||||
if (usRelease.release_date.Year != 1)
|
||||
{
|
||||
|
@ -733,7 +715,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
movie.ProductionYear = usRelease.release_date.Year;
|
||||
}
|
||||
}
|
||||
else if (minimunRelease.release_date != default (DateTime))
|
||||
else if (minimunRelease.release_date != default(DateTime))
|
||||
{
|
||||
if (minimunRelease.release_date.Year != 1)
|
||||
{
|
||||
|
@ -1099,7 +1081,7 @@ namespace MediaBrowser.Providers.Movies
|
|||
/// <value>The total_results.</value>
|
||||
public int total_results { get; set; }
|
||||
}
|
||||
|
||||
|
||||
protected class BelongsToCollection
|
||||
{
|
||||
public int id { get; set; }
|
||||
|
|
|
@ -279,13 +279,8 @@ namespace MediaBrowser.Providers.Movies
|
|||
{
|
||||
var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var img = await DownloadAndSaveImage(person, tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedProfileSize + profile.file_path,
|
||||
"folder" + Path.GetExtension(profile.file_path), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!string.IsNullOrEmpty(img))
|
||||
{
|
||||
person.PrimaryImagePath = img;
|
||||
}
|
||||
await DownloadAndSaveImage(person, tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedProfileSize + profile.file_path,
|
||||
MimeTypes.GetMimeType(profile.file_path), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -300,15 +295,12 @@ namespace MediaBrowser.Providers.Movies
|
|||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="targetName">Name of the target.</param>
|
||||
/// <param name="mimeType">Type of the MIME.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{System.String}.</returns>
|
||||
private async Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, CancellationToken cancellationToken)
|
||||
private async Task DownloadAndSaveImage(BaseItem item, string source, string mimeType, CancellationToken cancellationToken)
|
||||
{
|
||||
if (source == null) return null;
|
||||
|
||||
//download and save locally (if not already there)
|
||||
var localPath = Path.Combine(item.MetaLocation, targetName);
|
||||
if (source == null) return;
|
||||
|
||||
using (var sourceStream = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
|
||||
{
|
||||
|
@ -317,12 +309,11 @@ namespace MediaBrowser.Providers.Movies
|
|||
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
await ProviderManager.SaveToLibraryFilesystem(item, localPath, sourceStream, cancellationToken).ConfigureAwait(false);
|
||||
await ProviderManager.SaveImage(item, sourceStream, mimeType, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Logger.Debug("TmdbPersonProvider downloaded and saved image for {0}", item.Name);
|
||||
}
|
||||
|
||||
return localPath;
|
||||
}
|
||||
|
||||
#region Result Objects
|
||||
|
|
|
@ -199,7 +199,8 @@ namespace MediaBrowser.Providers.Music
|
|||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(item, path, DiscFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Disc, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +218,8 @@ namespace MediaBrowser.Providers.Music
|
|||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, path, PrimaryFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -306,7 +306,8 @@ namespace MediaBrowser.Providers.Music
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LogoFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Logo, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -323,7 +324,8 @@ namespace MediaBrowser.Providers.Music
|
|||
path = node.Value;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("Backdrop" + (numBackdrops > 0 ? numBackdrops.ToString(UsCulture) : "") + ".jpg"), SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Backdrop, numBackdrops, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
numBackdrops++;
|
||||
if (numBackdrops >= ConfigurationManager.Configuration.MaxBackdrops) break;
|
||||
}
|
||||
|
@ -343,7 +345,8 @@ namespace MediaBrowser.Providers.Music
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ArtFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Art, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -355,7 +358,8 @@ namespace MediaBrowser.Providers.Music
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BannerFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Banner, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,7 +372,8 @@ namespace MediaBrowser.Providers.Music
|
|||
path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, path, PrimaryFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,13 +82,16 @@ namespace MediaBrowser.Providers.Savers
|
|||
builder.Append("<Airs_Time>" + SecurityElement.Escape(series.AirTime) + "</Airs_Time>");
|
||||
}
|
||||
|
||||
if (series.AirDays.Count == 7)
|
||||
if (series.AirDays != null)
|
||||
{
|
||||
builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape("Daily") + "</Airs_DayOfWeek>");
|
||||
}
|
||||
else if (series.AirDays.Count > 0)
|
||||
{
|
||||
builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</Airs_DayOfWeek>");
|
||||
if (series.AirDays.Count == 7)
|
||||
{
|
||||
builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape("Daily") + "</Airs_DayOfWeek>");
|
||||
}
|
||||
else if (series.AirDays.Count > 0)
|
||||
{
|
||||
builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</Airs_DayOfWeek>");
|
||||
}
|
||||
}
|
||||
|
||||
XmlSaverHelpers.AddCommonNodes(item, builder);
|
||||
|
|
|
@ -149,7 +149,8 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
season.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(season, path, ThumbFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(season, path, FanArtResourcePool, ImageType.Thumb, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,7 +234,8 @@ namespace MediaBrowser.Providers.TV
|
|||
var path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LogoFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Logo, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +251,8 @@ namespace MediaBrowser.Providers.TV
|
|||
var path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ArtFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Art, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +265,8 @@ namespace MediaBrowser.Providers.TV
|
|||
var path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(item, path, ThumbFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Thumb, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,7 +277,8 @@ namespace MediaBrowser.Providers.TV
|
|||
var path = node != null ? node.Value : null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BannerFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Banner, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +296,8 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("backdrop" + (numBackdrops > 0 ? numBackdrops.ToString(UsCulture) : "") + ".jpg"), ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Backdrop, numBackdrops, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
numBackdrops++;
|
||||
|
||||
|
|
|
@ -276,7 +276,10 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
try
|
||||
{
|
||||
episode.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(episode, TVUtils.BannerUrl + p, Path.GetFileName(p), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken);
|
||||
var url = TVUtils.BannerUrl + p;
|
||||
|
||||
await _providerManager.SaveImage(episode, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException)
|
||||
{
|
||||
|
|
|
@ -188,7 +188,12 @@ namespace MediaBrowser.Providers.TV
|
|||
n = n.SelectSingleNode("./BannerPath");
|
||||
|
||||
if (n != null)
|
||||
season.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false);
|
||||
{
|
||||
var url = TVUtils.BannerUrl + n.InnerText;
|
||||
|
||||
await _providerManager.SaveImage(season, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,15 +208,10 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
try
|
||||
{
|
||||
var bannerImagePath =
|
||||
await _providerManager.DownloadAndSaveImage(season,
|
||||
TVUtils.BannerUrl + n.InnerText,
|
||||
"banner" +
|
||||
Path.GetExtension(n.InnerText),
|
||||
ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).
|
||||
ConfigureAwait(false);
|
||||
var url = TVUtils.BannerUrl + n.InnerText;
|
||||
|
||||
season.SetImage(ImageType.Banner, bannerImagePath);
|
||||
await _providerManager.SaveImage(season, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
|
@ -235,7 +235,11 @@ namespace MediaBrowser.Providers.TV
|
|||
n = n.SelectSingleNode("./BannerPath");
|
||||
if (n != null)
|
||||
{
|
||||
season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
var url = TVUtils.BannerUrl + n.InnerText;
|
||||
|
||||
await _providerManager.SaveImage(season, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, 0, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -215,13 +215,6 @@ namespace MediaBrowser.Providers.TV
|
|||
await FetchSeriesData(series, seriesId, seriesDataPath, force, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
BaseProviderInfo data;
|
||||
if (!item.ProviderData.TryGetValue(Id, out data))
|
||||
{
|
||||
data = new BaseProviderInfo();
|
||||
item.ProviderData[Id] = data;
|
||||
}
|
||||
|
||||
SetLastRefreshed(item, DateTime.UtcNow);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -197,9 +197,10 @@ namespace MediaBrowser.Providers.TV
|
|||
n = n.SelectSingleNode("./BannerPath");
|
||||
if (n != null)
|
||||
{
|
||||
var path = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false);
|
||||
var url = TVUtils.BannerUrl + n.InnerText;
|
||||
|
||||
series.SetImage(ImageType.Primary, path);
|
||||
await _providerManager.SaveImage(series, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,9 +213,10 @@ namespace MediaBrowser.Providers.TV
|
|||
n = n.SelectSingleNode("./BannerPath");
|
||||
if (n != null)
|
||||
{
|
||||
var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken);
|
||||
var url = TVUtils.BannerUrl + n.InnerText;
|
||||
|
||||
series.SetImage(ImageType.Banner, bannerImagePath);
|
||||
await _providerManager.SaveImage(series, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,8 +234,11 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
if (p != null)
|
||||
{
|
||||
var bdName = "backdrop" + (bdNo > 0 ? bdNo.ToString(UsCulture) : "");
|
||||
series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false));
|
||||
var url = TVUtils.BannerUrl + p.InnerText;
|
||||
|
||||
await _providerManager.SaveImage(series, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, bdNo, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
bdNo++;
|
||||
}
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
255
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
Normal file
255
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|||
/// </summary>
|
||||
public class ProviderManager : IProviderManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The remote image cache
|
||||
/// </summary>
|
||||
private readonly FileSystemRepository _remoteImageCache;
|
||||
|
||||
/// <summary>
|
||||
/// The currently running metadata providers
|
||||
/// </summary>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user