aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api/Images
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api/Images')
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs160
-rw-r--r--MediaBrowser.Api/Images/RemoteImageService.cs297
2 files changed, 144 insertions, 313 deletions
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index fdb15d96a..0b8ddeacd 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -18,9 +18,11 @@ using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
+using User = Jellyfin.Data.Entities.User;
namespace MediaBrowser.Api.Images
{
@@ -408,14 +410,14 @@ namespace MediaBrowser.Api.Images
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, Guid.Empty, item, false);
+ return GetImage(request, item, false);
}
public object Head(GetUserImage request)
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, Guid.Empty, item, true);
+ return GetImage(request, item, true);
}
public object Get(GetItemByNameImage request)
@@ -448,9 +450,9 @@ namespace MediaBrowser.Api.Images
request.Type = Enum.Parse<ImageType>(GetPathValue(3).ToString(), true);
- var item = _userManager.GetUserById(id);
+ var user = _userManager.GetUserById(id);
- return PostImage(item, request.RequestStream, request.Type, Request.ContentType);
+ return PostImage(user, request.RequestStream, Request.ContentType);
}
/// <summary>
@@ -477,9 +479,17 @@ namespace MediaBrowser.Api.Images
var userId = request.Id;
AssertCanUpdateUser(_authContext, _userManager, userId, true);
- var item = _userManager.GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
+ try
+ {
+ File.Delete(user.ProfileImage.Path);
+ }
+ catch (IOException e)
+ {
+ Logger.LogError(e, "Error deleting user profile image:");
+ }
- item.DeleteImage(request.Type, request.Index ?? 0);
+ _userManager.ClearProfileImage(user);
}
/// <summary>
@@ -567,14 +577,14 @@ namespace MediaBrowser.Api.Images
throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
}
- bool cropwhitespace;
+ bool cropWhitespace;
if (request.CropWhitespace.HasValue)
{
- cropwhitespace = request.CropWhitespace.Value;
+ cropWhitespace = request.CropWhitespace.Value;
}
else
{
- cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
+ cropWhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
}
var outputFormats = GetOutputFormats(request);
@@ -597,7 +607,35 @@ namespace MediaBrowser.Api.Images
itemId,
request,
imageInfo,
- cropwhitespace,
+ cropWhitespace,
+ outputFormats,
+ cacheDuration,
+ responseHeaders,
+ isHeadRequest);
+ }
+
+ public Task<object> GetImage(ImageRequest request, User user, bool isHeadRequest)
+ {
+ var imageInfo = GetImageInfo(request, user);
+
+ TimeSpan? cacheDuration = null;
+
+ if (!string.IsNullOrEmpty(request.Tag))
+ {
+ cacheDuration = TimeSpan.FromDays(365);
+ }
+
+ var responseHeaders = new Dictionary<string, string>
+ {
+ {"transferMode.dlna.org", "Interactive"},
+ {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"}
+ };
+
+ var outputFormats = GetOutputFormats(request);
+
+ return GetImageResult(user.Id,
+ request,
+ imageInfo,
outputFormats,
cacheDuration,
responseHeaders,
@@ -605,6 +643,55 @@ namespace MediaBrowser.Api.Images
}
private async Task<object> GetImageResult(
+ Guid itemId,
+ ImageRequest request,
+ ItemImageInfo info,
+ IReadOnlyCollection<ImageFormat> supportedFormats,
+ TimeSpan? cacheDuration,
+ IDictionary<string, string> headers,
+ bool isHeadRequest)
+ {
+ info.Type = ImageType.Profile;
+ var options = new ImageProcessingOptions
+ {
+ CropWhiteSpace = true,
+ Height = request.Height,
+ ImageIndex = request.Index ?? 0,
+ Image = info,
+ Item = null, // Hack alert
+ ItemId = itemId,
+ MaxHeight = request.MaxHeight,
+ MaxWidth = request.MaxWidth,
+ Quality = request.Quality ?? 100,
+ Width = request.Width,
+ AddPlayedIndicator = request.AddPlayedIndicator,
+ PercentPlayed = 0,
+ UnplayedCount = request.UnplayedCount,
+ Blur = request.Blur,
+ BackgroundColor = request.BackgroundColor,
+ ForegroundLayer = request.ForegroundLayer,
+ SupportedOutputFormats = supportedFormats
+ };
+
+ var imageResult = await _imageProcessor.ProcessImage(options).ConfigureAwait(false);
+
+ headers[HeaderNames.Vary] = HeaderNames.Accept;
+
+ return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
+ {
+ CacheDuration = cacheDuration,
+ ResponseHeaders = headers,
+ ContentType = imageResult.Item2,
+ DateLastModified = imageResult.Item3,
+ IsHeadRequest = isHeadRequest,
+ Path = imageResult.Item1,
+
+ FileShare = FileShare.Read
+
+ }).ConfigureAwait(false);
+ }
+
+ private async Task<object> GetImageResult(
BaseItem item,
Guid itemId,
ImageRequest request,
@@ -741,13 +828,35 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- private ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
+ private static ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
{
var index = request.Index ?? 0;
return item.GetImageInfo(request.Type, index);
}
+ private static ItemImageInfo GetImageInfo(ImageRequest request, User user)
+ {
+ var info = new ItemImageInfo
+ {
+ Path = user.ProfileImage.Path,
+ Type = ImageType.Primary,
+ DateModified = user.ProfileImage.LastModified,
+ };
+
+ if (request.Width.HasValue)
+ {
+ info.Width = request.Width.Value;
+ }
+
+ if (request.Height.HasValue)
+ {
+ info.Height = request.Height.Value;
+ }
+
+ return info;
+ }
+
/// <summary>
/// Posts the image.
/// </summary>
@@ -758,22 +867,41 @@ namespace MediaBrowser.Api.Images
/// <returns>Task.</returns>
public async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
{
+ var memoryStream = await GetMemoryStream(inputStream);
+
+ // Handle image/png; charset=utf-8
+ mimeType = mimeType.Split(';').FirstOrDefault();
+
+ await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
+
+ entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ }
+
+ private static async Task<MemoryStream> GetMemoryStream(Stream inputStream)
+ {
using var reader = new StreamReader(inputStream);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
var bytes = Convert.FromBase64String(text);
-
- var memoryStream = new MemoryStream(bytes)
+ return new MemoryStream(bytes)
{
Position = 0
};
+ }
+
+ private async Task PostImage(User user, Stream inputStream, string mimeType)
+ {
+ var memoryStream = await GetMemoryStream(inputStream);
// Handle image/png; charset=utf-8
mimeType = mimeType.Split(';').FirstOrDefault();
+ var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
+ user.ProfileImage = new Jellyfin.Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
- await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
-
- entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await _providerManager
+ .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path)
+ .ConfigureAwait(false);
+ await _userManager.UpdateUserAsync(user);
}
}
}
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
deleted file mode 100644
index 358ac30fa..000000000
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ /dev/null
@@ -1,297 +0,0 @@
-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);
- }
- }
-}