diff options
Diffstat (limited to 'MediaBrowser.Api/Images')
| -rw-r--r-- | MediaBrowser.Api/Images/ImageService.cs | 27 | ||||
| -rw-r--r-- | MediaBrowser.Api/Images/RemoteImageService.cs | 297 |
2 files changed, 318 insertions, 6 deletions
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index e8ea31251..fdb15d96a 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; @@ -280,9 +281,16 @@ namespace MediaBrowser.Api.Images public List<ImageInfo> GetItemImageInfos(BaseItem item) { var list = new List<ImageInfo>(); - var itemImages = item.ImageInfos; + if (itemImages.Length == 0) + { + // short-circuit + return list; + } + + _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct + foreach (var image in itemImages) { if (!item.AllowsMultipleImages(image.Type)) @@ -323,6 +331,7 @@ namespace MediaBrowser.Api.Images { int? width = null; int? height = null; + string blurhash = null; long length = 0; try @@ -332,9 +341,9 @@ namespace MediaBrowser.Api.Images var fileInfo = _fileSystem.GetFileInfo(info.Path); length = fileInfo.Length; - ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true); - width = size.Width; - height = size.Height; + blurhash = info.BlurHash; + width = info.Width; + height = info.Height; if (width <= 0 || height <= 0) { @@ -357,6 +366,7 @@ namespace MediaBrowser.Api.Images ImageType = info.Type, ImageTag = _imageProcessor.GetImageCacheTag(item, info), Size = length, + BlurHash = blurhash, Width = width, Height = height }; @@ -554,8 +564,7 @@ namespace MediaBrowser.Api.Images var imageInfo = GetImageInfo(request, item); if (imageInfo == null) { - var displayText = item == null ? itemId.ToString() : item.Name; - throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type)); + throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type)); } bool cropwhitespace; @@ -606,6 +615,12 @@ namespace MediaBrowser.Api.Images IDictionary<string, string> headers, bool isHeadRequest) { + if (!image.IsLocalFile) + { + item ??= _libraryManager.GetItemById(itemId); + image = await _libraryManager.ConvertImageToLocal(item, image, request.Index ?? 0).ConfigureAwait(false); + } + var options = new ImageProcessingOptions { CropWhiteSpace = cropwhitespace, diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs new file mode 100644 index 000000000..358ac30fa --- /dev/null +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Services; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Api.Images +{ + public class BaseRemoteImageRequest : IReturn<RemoteImageResult> + { + [ApiMember(Name = "Type", Description = "The image type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public ImageType? Type { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } + + [ApiMember(Name = "ProviderName", Description = "Optional. The image provider to use", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ProviderName { get; set; } + + [ApiMember(Name = "IncludeAllLanguages", Description = "Optional.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool IncludeAllLanguages { get; set; } + } + + [Route("/Items/{Id}/RemoteImages", "GET", Summary = "Gets available remote images for an item")] + [Authenticated] + public class GetRemoteImages : BaseRemoteImageRequest + { + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Items/{Id}/RemoteImages/Providers", "GET", Summary = "Gets available remote image providers for an item")] + [Authenticated] + public class GetRemoteImageProviders : IReturn<List<ImageProviderInfo>> + { + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + } + + public class BaseDownloadRemoteImage : IReturnVoid + { + [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET,POST")] + public ImageType Type { get; set; } + + [ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] + public string ProviderName { get; set; } + + [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] + public string ImageUrl { get; set; } + } + + [Route("/Items/{Id}/RemoteImages/Download", "POST", Summary = "Downloads a remote image for an item")] + [Authenticated(Roles = "Admin")] + public class DownloadRemoteImage : BaseDownloadRemoteImage + { + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public string Id { get; set; } + } + + [Route("/Images/Remote", "GET", Summary = "Gets a remote image")] + public class GetRemoteImage + { + [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ImageUrl { get; set; } + } + + public class RemoteImageService : BaseApiService + { + private readonly IProviderManager _providerManager; + + private readonly IServerApplicationPaths _appPaths; + private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + + private readonly ILibraryManager _libraryManager; + + public RemoteImageService( + ILogger<RemoteImageService> logger, + IServerConfigurationManager serverConfigurationManager, + IHttpResultFactory httpResultFactory, + IProviderManager providerManager, + IServerApplicationPaths appPaths, + IHttpClient httpClient, + IFileSystem fileSystem, + ILibraryManager libraryManager) + : base(logger, serverConfigurationManager, httpResultFactory) + { + _providerManager = providerManager; + _appPaths = appPaths; + _httpClient = httpClient; + _fileSystem = fileSystem; + _libraryManager = libraryManager; + } + + public object Get(GetRemoteImageProviders request) + { + var item = _libraryManager.GetItemById(request.Id); + + var result = GetImageProviders(item); + + return ToOptimizedResult(result); + } + + private List<ImageProviderInfo> GetImageProviders(BaseItem item) + { + return _providerManager.GetRemoteImageProviderInfo(item).ToList(); + } + + public async Task<object> Get(GetRemoteImages request) + { + var item = _libraryManager.GetItemById(request.Id); + + var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery(request.ProviderName) + { + IncludeAllLanguages = request.IncludeAllLanguages, + IncludeDisabledProviders = true, + ImageType = request.Type + + }, CancellationToken.None).ConfigureAwait(false); + + var imagesList = images.ToArray(); + + var allProviders = _providerManager.GetRemoteImageProviderInfo(item); + + if (request.Type.HasValue) + { + allProviders = allProviders.Where(i => i.SupportedImages.Contains(request.Type.Value)); + } + + var result = new RemoteImageResult + { + TotalRecordCount = imagesList.Length, + Providers = allProviders.Select(i => i.Name) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToArray() + }; + + if (request.StartIndex.HasValue) + { + imagesList = imagesList.Skip(request.StartIndex.Value) + .ToArray(); + } + + if (request.Limit.HasValue) + { + imagesList = imagesList.Take(request.Limit.Value) + .ToArray(); + } + + result.Images = imagesList; + + return result; + } + + /// <summary> + /// Posts the specified request. + /// </summary> + /// <param name="request">The request.</param> + public Task Post(DownloadRemoteImage request) + { + var item = _libraryManager.GetItemById(request.Id); + + return DownloadRemoteImage(item, request); + } + + /// <summary> + /// Downloads the remote image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="request">The request.</param> + /// <returns>Task.</returns> + private async Task DownloadRemoteImage(BaseItem item, BaseDownloadRemoteImage request) + { + await _providerManager.SaveImage(item, request.ImageUrl, request.Type, null, CancellationToken.None).ConfigureAwait(false); + + item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + } + + /// <summary> + /// Gets the specified request. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>System.Object.</returns> + public async Task<object> Get(GetRemoteImage request) + { + var urlHash = request.ImageUrl.GetMD5(); + var pointerCachePath = GetFullCachePath(urlHash.ToString()); + + string contentPath; + + try + { + contentPath = File.ReadAllText(pointerCachePath); + + if (File.Exists(contentPath)) + { + return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false); + } + } + catch (FileNotFoundException) + { + // Means the file isn't cached yet + } + catch (IOException) + { + // Means the file isn't cached yet + } + + await DownloadImage(request.ImageUrl, urlHash, pointerCachePath).ConfigureAwait(false); + + // Read the pointer file again + contentPath = File.ReadAllText(pointerCachePath); + + return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false); + } + + /// <summary> + /// Downloads the image. + /// </summary> + /// <param name="url">The URL.</param> + /// <param name="urlHash">The URL hash.</param> + /// <param name="pointerCachePath">The pointer cache path.</param> + /// <returns>Task.</returns> + private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath) + { + using var result = await _httpClient.GetResponse(new HttpRequestOptions + { + Url = url, + BufferContent = false + }).ConfigureAwait(false); + var ext = result.ContentType.Split('/')[^1]; + + var fullCachePath = GetFullCachePath(urlHash + "." + ext); + + Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); + var stream = result.Content; + await using (stream.ConfigureAwait(false)) + { + var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await using (filestream.ConfigureAwait(false)) + { + await stream.CopyToAsync(filestream).ConfigureAwait(false); + } + } + + Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); + File.WriteAllText(pointerCachePath, fullCachePath); + } + + /// <summary> + /// Gets the full cache path. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>System.String.</returns> + private string GetFullCachePath(string filename) + { + return Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename); + } + } +} |
