From e151d539f2041fb249af82118bde1168d1859c6b Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 20 Apr 2020 13:06:29 -0600 Subject: [PATCH 01/11] Move ImageByNameService to Jellyfin.Api --- .../Images/ImageByNameController.cs | 261 +++++++++++++++++ MediaBrowser.Api/Images/ImageByNameService.cs | 277 ------------------ 2 files changed, 261 insertions(+), 277 deletions(-) create mode 100644 Jellyfin.Api/Controllers/Images/ImageByNameController.cs delete mode 100644 MediaBrowser.Api/Images/ImageByNameService.cs diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs new file mode 100644 index 0000000000..a14e2403c4 --- /dev/null +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -0,0 +1,261 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers.Images +{ + /// + /// Images By Name Controller. + /// + [Route("Images")] + [Authenticated] + public class ImageByNameController : BaseJellyfinApiController + { + private readonly IServerApplicationPaths _applicationPaths; + private readonly IFileSystem _fileSystem; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + public ImageByNameController( + IServerConfigurationManager serverConfigurationManager, + IFileSystem fileSystem) + { + _applicationPaths = serverConfigurationManager.ApplicationPaths; + _fileSystem = fileSystem; + } + + /// + /// Get all general images. + /// + /// General images. + [HttpGet("General")] + [ProducesResponseType(typeof(ImageByNameInfo[]), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] + public IActionResult GetGeneralImages() + { + try + { + return Ok(GetImageList(_applicationPaths.GeneralPath, false)); + } + catch (Exception e) + { + return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + } + } + + /// + /// Get General Image. + /// + /// The name of the image. + /// Image Type (primary, backdrop, logo, etc). + /// Image Stream. + [HttpGet("General/{Name}/{Type}")] + [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] + public IActionResult GetGeneralImage([FromRoute] string name, [FromRoute] string type) + { + try + { + var filename = string.Equals(type, "primary", StringComparison.OrdinalIgnoreCase) + ? "folder" + : type; + + var paths = BaseItem.SupportedImageExtensions + .Select(i => Path.Combine(_applicationPaths.GeneralPath, name, filename + i)).ToList(); + + var path = paths.FirstOrDefault(System.IO.File.Exists) ?? paths.FirstOrDefault(); + if (path == null || !System.IO.File.Exists(path)) + { + return NotFound(); + } + + var contentType = MimeTypes.GetMimeType(path); + return new FileStreamResult(System.IO.File.OpenRead(path), contentType); + } + catch (Exception e) + { + return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + } + } + + /// + /// Get all general images. + /// + /// General images. + [HttpGet("Ratings")] + [ProducesResponseType(typeof(ImageByNameInfo[]), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] + public IActionResult GetRatingImages() + { + try + { + return Ok(GetImageList(_applicationPaths.RatingsPath, false)); + } + catch (Exception e) + { + return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + } + } + + /// + /// Get rating image. + /// + /// The theme to get the image from. + /// The name of the image. + /// Image Stream. + [HttpGet("Ratings/{Theme}/{Name}")] + [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] + public IActionResult GetRatingImage( + [FromRoute] string theme, + [FromRoute] string name) + { + try + { + return GetImageFile(_applicationPaths.RatingsPath, theme, name); + } + catch (Exception e) + { + return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + } + } + + /// + /// Get all media info images. + /// + /// Media Info images. + [HttpGet("MediaInfo")] + [ProducesResponseType(typeof(ImageByNameInfo[]), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] + public IActionResult GetMediaInfoImages() + { + try + { + return Ok(GetImageList(_applicationPaths.MediaInfoImagesPath, false)); + } + catch (Exception e) + { + return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + } + } + + /// + /// Get media info image. + /// + /// The theme to get the image from. + /// The name of the image. + /// Image Stream. + [HttpGet("MediaInfo/{Theme}/{Name}")] + [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] + public IActionResult GetMediaInfoImage( + [FromRoute] string theme, + [FromRoute] string name) + { + try + { + return GetImageFile(_applicationPaths.MediaInfoImagesPath, theme, name); + } + catch (Exception e) + { + return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + } + } + + /// + /// Internal FileHelper. + /// + /// Path to begin search. + /// Theme to search. + /// File name to search for. + /// Image Stream. + private IActionResult GetImageFile(string basePath, string theme, string name) + { + var themeFolder = Path.Combine(basePath, theme); + if (Directory.Exists(themeFolder)) + { + var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, name + i)) + .FirstOrDefault(System.IO.File.Exists); + + if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) + { + var contentType = MimeTypes.GetMimeType(path); + return new FileStreamResult(System.IO.File.OpenRead(path), contentType); + } + } + + var allFolder = Path.Combine(basePath, "all"); + if (Directory.Exists(allFolder)) + { + var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, name + i)) + .FirstOrDefault(System.IO.File.Exists); + + if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) + { + var contentType = MimeTypes.GetMimeType(path); + return new FileStreamResult(System.IO.File.OpenRead(path), contentType); + } + } + + return NotFound(); + } + + private List GetImageList(string path, bool supportsThemes) + { + try + { + return _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, true) + .Select(i => new ImageByNameInfo + { + Name = _fileSystem.GetFileNameWithoutExtension(i), + FileLength = i.Length, + + // For themeable images, use the Theme property + // For general images, the same object structure is fine, + // but it's not owned by a theme, so call it Context + Theme = supportsThemes ? GetThemeName(i.FullName, path) : null, + Context = supportsThemes ? null : GetThemeName(i.FullName, path), + Format = i.Extension.ToLowerInvariant().TrimStart('.') + }) + .OrderBy(i => i.Name) + .ToList(); + } + catch (IOException) + { + return new List(); + } + } + + private string GetThemeName(string path, string rootImagePath) + { + var parentName = Path.GetDirectoryName(path); + + if (string.Equals(parentName, rootImagePath, StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + parentName = Path.GetFileName(parentName); + + return string.Equals(parentName, "all", StringComparison.OrdinalIgnoreCase) ? null : parentName; + } + } +} diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs deleted file mode 100644 index 45b7d0c100..0000000000 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.Images -{ - /// - /// Class GetGeneralImage - /// - [Route("/Images/General/{Name}/{Type}", "GET", Summary = "Gets a general image by name")] - public class GetGeneralImage - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The name of the image", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - [ApiMember(Name = "Type", Description = "Image Type (primary, backdrop, logo, etc).", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Type { get; set; } - } - - /// - /// Class GetRatingImage - /// - [Route("/Images/Ratings/{Theme}/{Name}", "GET", Summary = "Gets a rating image by name")] - public class GetRatingImage - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The name of the image", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// - /// Gets or sets the theme. - /// - /// The theme. - [ApiMember(Name = "Theme", Description = "The theme to get the image from", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Theme { get; set; } - } - - /// - /// Class GetMediaInfoImage - /// - [Route("/Images/MediaInfo/{Theme}/{Name}", "GET", Summary = "Gets a media info image by name")] - public class GetMediaInfoImage - { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "Name", Description = "The name of the image", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// - /// Gets or sets the theme. - /// - /// The theme. - [ApiMember(Name = "Theme", Description = "The theme to get the image from", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Theme { get; set; } - } - - [Route("/Images/MediaInfo", "GET", Summary = "Gets all media info image by name")] - [Authenticated] - public class GetMediaInfoImages : IReturn> - { - } - - [Route("/Images/Ratings", "GET", Summary = "Gets all rating images by name")] - [Authenticated] - public class GetRatingImages : IReturn> - { - } - - [Route("/Images/General", "GET", Summary = "Gets all general images by name")] - [Authenticated] - public class GetGeneralImages : IReturn> - { - } - - /// - /// Class ImageByNameService - /// - public class ImageByNameService : BaseApiService - { - /// - /// The _app paths - /// - private readonly IServerApplicationPaths _appPaths; - - private readonly IFileSystem _fileSystem; - - /// - /// Initializes a new instance of the class. - /// - public ImageByNameService( - ILogger logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory resultFactory, - IFileSystem fileSystem) - : base(logger, serverConfigurationManager, resultFactory) - { - _appPaths = serverConfigurationManager.ApplicationPaths; - _fileSystem = fileSystem; - } - - public object Get(GetMediaInfoImages request) - { - return ToOptimizedResult(GetImageList(_appPaths.MediaInfoImagesPath, true)); - } - - public object Get(GetRatingImages request) - { - return ToOptimizedResult(GetImageList(_appPaths.RatingsPath, true)); - } - - public object Get(GetGeneralImages request) - { - return ToOptimizedResult(GetImageList(_appPaths.GeneralPath, false)); - } - - private List GetImageList(string path, bool supportsThemes) - { - try - { - return _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, true) - .Select(i => new ImageByNameInfo - { - Name = _fileSystem.GetFileNameWithoutExtension(i), - FileLength = i.Length, - - // For themeable images, use the Theme property - // For general images, the same object structure is fine, - // but it's not owned by a theme, so call it Context - Theme = supportsThemes ? GetThemeName(i.FullName, path) : null, - Context = supportsThemes ? null : GetThemeName(i.FullName, path), - - Format = i.Extension.ToLowerInvariant().TrimStart('.') - }) - .OrderBy(i => i.Name) - .ToList(); - } - catch (IOException) - { - return new List(); - } - } - - private string GetThemeName(string path, string rootImagePath) - { - var parentName = Path.GetDirectoryName(path); - - if (string.Equals(parentName, rootImagePath, StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - parentName = Path.GetFileName(parentName); - - return string.Equals(parentName, "all", StringComparison.OrdinalIgnoreCase) ? - null : - parentName; - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public Task Get(GetGeneralImage request) - { - var filename = string.Equals(request.Type, "primary", StringComparison.OrdinalIgnoreCase) - ? "folder" - : request.Type; - - var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList(); - - var path = paths.FirstOrDefault(File.Exists) ?? paths.FirstOrDefault(); - - return ResultFactory.GetStaticFileResult(Request, path); - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetRatingImage request) - { - var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme); - - if (Directory.Exists(themeFolder)) - { - var path = BaseItem.SupportedImageExtensions - .Select(i => Path.Combine(themeFolder, request.Name + i)) - .FirstOrDefault(File.Exists); - - if (!string.IsNullOrEmpty(path)) - { - return ResultFactory.GetStaticFileResult(Request, path); - } - } - - var allFolder = Path.Combine(_appPaths.RatingsPath, "all"); - - if (Directory.Exists(allFolder)) - { - // Avoid implicitly captured closure - var currentRequest = request; - - var path = BaseItem.SupportedImageExtensions - .Select(i => Path.Combine(allFolder, currentRequest.Name + i)) - .FirstOrDefault(File.Exists); - - if (!string.IsNullOrEmpty(path)) - { - return ResultFactory.GetStaticFileResult(Request, path); - } - } - - throw new ResourceNotFoundException("MediaInfo image not found: " + request.Name); - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public Task Get(GetMediaInfoImage request) - { - var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme); - - if (Directory.Exists(themeFolder)) - { - var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i)) - .FirstOrDefault(File.Exists); - - if (!string.IsNullOrEmpty(path)) - { - return ResultFactory.GetStaticFileResult(Request, path); - } - } - - var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all"); - - if (Directory.Exists(allFolder)) - { - // Avoid implicitly captured closure - var currentRequest = request; - - var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i)) - .FirstOrDefault(File.Exists); - - if (!string.IsNullOrEmpty(path)) - { - return ResultFactory.GetStaticFileResult(Request, path); - } - } - - throw new ResourceNotFoundException("MediaInfo image not found: " + request.Name); - } - } -} From 376619369d8b1e889475da1191092f43e7f26ae6 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 20 Apr 2020 13:12:35 -0600 Subject: [PATCH 02/11] fix build --- Jellyfin.Api/Controllers/Images/ImageByNameController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index a14e2403c4..3097296051 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -244,7 +244,7 @@ namespace Jellyfin.Api.Controllers.Images } } - private string GetThemeName(string path, string rootImagePath) + private string? GetThemeName(string path, string rootImagePath) { var parentName = Path.GetDirectoryName(path); From 30609236ab58532d021e45edcdacd32d78aeca94 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 21 Apr 2020 07:57:45 -0600 Subject: [PATCH 03/11] Remove exception handler --- .../Images/ImageByNameController.cs | 74 ++++--------------- 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index 3097296051..4034c9e857 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -48,14 +48,7 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] public IActionResult GetGeneralImages() { - try - { - return Ok(GetImageList(_applicationPaths.GeneralPath, false)); - } - catch (Exception e) - { - return StatusCode(StatusCodes.Status500InternalServerError, e.Message); - } + return Ok(GetImageList(_applicationPaths.GeneralPath, false)); } /// @@ -70,28 +63,21 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] public IActionResult GetGeneralImage([FromRoute] string name, [FromRoute] string type) { - try - { - var filename = string.Equals(type, "primary", StringComparison.OrdinalIgnoreCase) - ? "folder" - : type; - - var paths = BaseItem.SupportedImageExtensions - .Select(i => Path.Combine(_applicationPaths.GeneralPath, name, filename + i)).ToList(); + var filename = string.Equals(type, "primary", StringComparison.OrdinalIgnoreCase) + ? "folder" + : type; - var path = paths.FirstOrDefault(System.IO.File.Exists) ?? paths.FirstOrDefault(); - if (path == null || !System.IO.File.Exists(path)) - { - return NotFound(); - } + var paths = BaseItem.SupportedImageExtensions + .Select(i => Path.Combine(_applicationPaths.GeneralPath, name, filename + i)).ToList(); - var contentType = MimeTypes.GetMimeType(path); - return new FileStreamResult(System.IO.File.OpenRead(path), contentType); - } - catch (Exception e) + var path = paths.FirstOrDefault(System.IO.File.Exists) ?? paths.FirstOrDefault(); + if (path == null || !System.IO.File.Exists(path)) { - return StatusCode(StatusCodes.Status500InternalServerError, e.Message); + return NotFound(); } + + var contentType = MimeTypes.GetMimeType(path); + return new FileStreamResult(System.IO.File.OpenRead(path), contentType); } /// @@ -103,14 +89,7 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] public IActionResult GetRatingImages() { - try - { - return Ok(GetImageList(_applicationPaths.RatingsPath, false)); - } - catch (Exception e) - { - return StatusCode(StatusCodes.Status500InternalServerError, e.Message); - } + return Ok(GetImageList(_applicationPaths.RatingsPath, false)); } /// @@ -127,14 +106,7 @@ namespace Jellyfin.Api.Controllers.Images [FromRoute] string theme, [FromRoute] string name) { - try - { - return GetImageFile(_applicationPaths.RatingsPath, theme, name); - } - catch (Exception e) - { - return StatusCode(StatusCodes.Status500InternalServerError, e.Message); - } + return GetImageFile(_applicationPaths.RatingsPath, theme, name); } /// @@ -146,14 +118,7 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] public IActionResult GetMediaInfoImages() { - try - { - return Ok(GetImageList(_applicationPaths.MediaInfoImagesPath, false)); - } - catch (Exception e) - { - return StatusCode(StatusCodes.Status500InternalServerError, e.Message); - } + return Ok(GetImageList(_applicationPaths.MediaInfoImagesPath, false)); } /// @@ -170,14 +135,7 @@ namespace Jellyfin.Api.Controllers.Images [FromRoute] string theme, [FromRoute] string name) { - try - { - return GetImageFile(_applicationPaths.MediaInfoImagesPath, theme, name); - } - catch (Exception e) - { - return StatusCode(StatusCodes.Status500InternalServerError, e.Message); - } + return GetImageFile(_applicationPaths.MediaInfoImagesPath, theme, name); } /// From 02a78aaae98bdecacd04325e124bde9224c66955 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 21 Apr 2020 14:07:11 -0600 Subject: [PATCH 04/11] move to ActionResult --- .../Images/ImageByNameController.cs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index 4034c9e857..ce509b4e6d 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -44,9 +44,8 @@ namespace Jellyfin.Api.Controllers.Images /// /// General images. [HttpGet("General")] - [ProducesResponseType(typeof(ImageByNameInfo[]), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] - public IActionResult GetGeneralImages() + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetGeneralImages() { return Ok(GetImageList(_applicationPaths.GeneralPath, false)); } @@ -58,10 +57,10 @@ namespace Jellyfin.Api.Controllers.Images /// Image Type (primary, backdrop, logo, etc). /// Image Stream. [HttpGet("General/{Name}/{Type}")] - [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] + [Produces("application/octet-stream")] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] - public IActionResult GetGeneralImage([FromRoute] string name, [FromRoute] string type) + public ActionResult GetGeneralImage([FromRoute] string name, [FromRoute] string type) { var filename = string.Equals(type, "primary", StringComparison.OrdinalIgnoreCase) ? "folder" @@ -85,9 +84,8 @@ namespace Jellyfin.Api.Controllers.Images /// /// General images. [HttpGet("Ratings")] - [ProducesResponseType(typeof(ImageByNameInfo[]), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] - public IActionResult GetRatingImages() + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetRatingImages() { return Ok(GetImageList(_applicationPaths.RatingsPath, false)); } @@ -99,10 +97,10 @@ namespace Jellyfin.Api.Controllers.Images /// The name of the image. /// Image Stream. [HttpGet("Ratings/{Theme}/{Name}")] - [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] + [Produces("application/octet-stream")] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] - public IActionResult GetRatingImage( + public ActionResult GetRatingImage( [FromRoute] string theme, [FromRoute] string name) { @@ -114,9 +112,8 @@ namespace Jellyfin.Api.Controllers.Images /// /// Media Info images. [HttpGet("MediaInfo")] - [ProducesResponseType(typeof(ImageByNameInfo[]), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] - public IActionResult GetMediaInfoImages() + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetMediaInfoImages() { return Ok(GetImageList(_applicationPaths.MediaInfoImagesPath, false)); } @@ -128,10 +125,10 @@ namespace Jellyfin.Api.Controllers.Images /// The name of the image. /// Image Stream. [HttpGet("MediaInfo/{Theme}/{Name}")] - [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] + [Produces("application/octet-stream")] + [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)] - public IActionResult GetMediaInfoImage( + public ActionResult GetMediaInfoImage( [FromRoute] string theme, [FromRoute] string name) { @@ -145,7 +142,7 @@ namespace Jellyfin.Api.Controllers.Images /// Theme to search. /// File name to search for. /// Image Stream. - private IActionResult GetImageFile(string basePath, string theme, string name) + private ActionResult GetImageFile(string basePath, string theme, string name) { var themeFolder = Path.Combine(basePath, theme); if (Directory.Exists(themeFolder)) From cbd4a64e670eda1c30be6000a8f6cceccc93ddfa Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 2 May 2020 18:46:27 -0600 Subject: [PATCH 05/11] Update endpoint docs --- .../Images/ImageByNameController.cs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index ce509b4e6d..6160d54028 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -42,10 +42,11 @@ namespace Jellyfin.Api.Controllers.Images /// /// Get all general images. /// - /// General images. + /// Retrieved list of images. + /// An containing the list of images. [HttpGet("General")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetGeneralImages() + public ActionResult> GetGeneralImages() { return Ok(GetImageList(_applicationPaths.GeneralPath, false)); } @@ -55,7 +56,9 @@ namespace Jellyfin.Api.Controllers.Images /// /// The name of the image. /// Image Type (primary, backdrop, logo, etc). - /// Image Stream. + /// Image stream retrieved. + /// Image not found. + /// A containing the image contents on success, or a if the image could not be found. [HttpGet("General/{Name}/{Type}")] [Produces("application/octet-stream")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -82,10 +85,11 @@ namespace Jellyfin.Api.Controllers.Images /// /// Get all general images. /// - /// General images. + /// Retrieved list of images. + /// An containing the list of images. [HttpGet("Ratings")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRatingImages() + public ActionResult> GetRatingImages() { return Ok(GetImageList(_applicationPaths.RatingsPath, false)); } @@ -95,7 +99,9 @@ namespace Jellyfin.Api.Controllers.Images /// /// The theme to get the image from. /// The name of the image. - /// Image Stream. + /// Image stream retrieved. + /// Image not found. + /// A containing the image contents on success, or a if the image could not be found. [HttpGet("Ratings/{Theme}/{Name}")] [Produces("application/octet-stream")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -110,7 +116,8 @@ namespace Jellyfin.Api.Controllers.Images /// /// Get all media info images. /// - /// Media Info images. + /// Image list retrieved. + /// An containing the list of images. [HttpGet("MediaInfo")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetMediaInfoImages() @@ -123,7 +130,9 @@ namespace Jellyfin.Api.Controllers.Images /// /// The theme to get the image from. /// The name of the image. - /// Image Stream. + /// Image stream retrieved. + /// Image not found. + /// A containing the image contents on success, or a if the image could not be found. [HttpGet("MediaInfo/{Theme}/{Name}")] [Produces("application/octet-stream")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -141,7 +150,7 @@ namespace Jellyfin.Api.Controllers.Images /// Path to begin search. /// Theme to search. /// File name to search for. - /// Image Stream. + /// A containing the image contents on success, or a if the image could not be found. private ActionResult GetImageFile(string basePath, string theme, string name) { var themeFolder = Path.Combine(basePath, theme); From 35dbcea9311589ea7b9a10ab02da557a2bfb46fc Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 2 May 2020 18:47:05 -0600 Subject: [PATCH 06/11] Return array -> ienumerable --- Jellyfin.Api/Controllers/Images/ImageByNameController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index 6160d54028..67ebaa4e09 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers.Images /// An containing the list of images. [HttpGet("MediaInfo")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetMediaInfoImages() + public ActionResult> GetMediaInfoImages() { return Ok(GetImageList(_applicationPaths.MediaInfoImagesPath, false)); } From 98bd61e36443452a280dc9d3543baecc10b561ed Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 19 May 2020 09:14:37 -0600 Subject: [PATCH 07/11] Clean up routes --- Jellyfin.Api/Controllers/Images/ImageByNameController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index 67ebaa4e09..62fcb5a2a6 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetGeneralImages() { - return Ok(GetImageList(_applicationPaths.GeneralPath, false)); + return GetImageList(_applicationPaths.GeneralPath, false); } /// @@ -91,7 +91,7 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetRatingImages() { - return Ok(GetImageList(_applicationPaths.RatingsPath, false)); + return GetImageList(_applicationPaths.RatingsPath, false); } /// @@ -122,7 +122,7 @@ namespace Jellyfin.Api.Controllers.Images [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetMediaInfoImages() { - return Ok(GetImageList(_applicationPaths.MediaInfoImagesPath, false)); + return GetImageList(_applicationPaths.MediaInfoImagesPath, false); } /// From 839de72f9a320bfe09cdd9c2fcab6806f3106916 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 19 May 2020 09:24:04 -0600 Subject: [PATCH 08/11] Fix authentication attribute --- Jellyfin.Api/Controllers/Images/ImageByNameController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index 62fcb5a2a6..dadb344385 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -7,10 +7,10 @@ using System.Linq; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -20,7 +20,7 @@ namespace Jellyfin.Api.Controllers.Images /// Images By Name Controller. /// [Route("Images")] - [Authenticated] + [Authorize] public class ImageByNameController : BaseJellyfinApiController { private readonly IServerApplicationPaths _applicationPaths; From 070edd9e5bff63d3f158b6ca8b37095adc686492 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 19 May 2020 13:13:07 -0600 Subject: [PATCH 09/11] Fix MediaType usage --- Jellyfin.Api/Controllers/Images/ImageByNameController.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index dadb344385..fa60809773 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Mime; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -60,7 +61,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image not found. /// A containing the image contents on success, or a if the image could not be found. [HttpGet("General/{Name}/{Type}")] - [Produces("application/octet-stream")] + [Produces(MediaTypeNames.Application.Octet)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetGeneralImage([FromRoute] string name, [FromRoute] string type) @@ -103,7 +104,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image not found. /// A containing the image contents on success, or a if the image could not be found. [HttpGet("Ratings/{Theme}/{Name}")] - [Produces("application/octet-stream")] + [Produces(MediaTypeNames.Application.Octet)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetRatingImage( @@ -134,7 +135,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image not found. /// A containing the image contents on success, or a if the image could not be found. [HttpGet("MediaInfo/{Theme}/{Name}")] - [Produces("application/octet-stream")] + [Produces(MediaTypeNames.Application.Octet)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetMediaInfoImage( From fd913d73e35491c64e39a76a266689c302a91b11 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 4 Jun 2020 10:10:36 -0600 Subject: [PATCH 10/11] Revert authorized endpoints to legacy api --- .../Controllers/Images/ImageByNameController.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs index fa60809773..db475d6b47 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/Images/ImageByNameController.cs @@ -21,7 +21,6 @@ namespace Jellyfin.Api.Controllers.Images /// Images By Name Controller. /// [Route("Images")] - [Authorize] public class ImageByNameController : BaseJellyfinApiController { private readonly IServerApplicationPaths _applicationPaths; @@ -46,6 +45,7 @@ namespace Jellyfin.Api.Controllers.Images /// Retrieved list of images. /// An containing the list of images. [HttpGet("General")] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetGeneralImages() { @@ -61,6 +61,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image not found. /// A containing the image contents on success, or a if the image could not be found. [HttpGet("General/{Name}/{Type}")] + [AllowAnonymous] [Produces(MediaTypeNames.Application.Octet)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -70,11 +71,11 @@ namespace Jellyfin.Api.Controllers.Images ? "folder" : type; - var paths = BaseItem.SupportedImageExtensions - .Select(i => Path.Combine(_applicationPaths.GeneralPath, name, filename + i)).ToList(); + var path = BaseItem.SupportedImageExtensions + .Select(i => Path.Combine(_applicationPaths.GeneralPath, name, filename + i)) + .FirstOrDefault(System.IO.File.Exists); - var path = paths.FirstOrDefault(System.IO.File.Exists) ?? paths.FirstOrDefault(); - if (path == null || !System.IO.File.Exists(path)) + if (path == null) { return NotFound(); } @@ -89,6 +90,7 @@ namespace Jellyfin.Api.Controllers.Images /// Retrieved list of images. /// An containing the list of images. [HttpGet("Ratings")] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetRatingImages() { @@ -104,6 +106,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image not found. /// A containing the image contents on success, or a if the image could not be found. [HttpGet("Ratings/{Theme}/{Name}")] + [AllowAnonymous] [Produces(MediaTypeNames.Application.Octet)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -120,6 +123,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image list retrieved. /// An containing the list of images. [HttpGet("MediaInfo")] + [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetMediaInfoImages() { @@ -135,6 +139,7 @@ namespace Jellyfin.Api.Controllers.Images /// Image not found. /// A containing the image contents on success, or a if the image could not be found. [HttpGet("MediaInfo/{Theme}/{Name}")] + [AllowAnonymous] [Produces(MediaTypeNames.Application.Octet)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] From 0d1298e851d3cdfc56b74f44dc94cfc981a4e8f3 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 17 Jun 2020 10:49:34 -0600 Subject: [PATCH 11/11] User proper File constructor --- .../Controllers/{Images => }/ImageByNameController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename Jellyfin.Api/Controllers/{Images => }/ImageByNameController.cs (96%) diff --git a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs b/Jellyfin.Api/Controllers/ImageByNameController.cs similarity index 96% rename from Jellyfin.Api/Controllers/Images/ImageByNameController.cs rename to Jellyfin.Api/Controllers/ImageByNameController.cs index db475d6b47..fa46b6dd17 100644 --- a/Jellyfin.Api/Controllers/Images/ImageByNameController.cs +++ b/Jellyfin.Api/Controllers/ImageByNameController.cs @@ -15,7 +15,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace Jellyfin.Api.Controllers.Images +namespace Jellyfin.Api.Controllers { /// /// Images By Name Controller. @@ -81,7 +81,7 @@ namespace Jellyfin.Api.Controllers.Images } var contentType = MimeTypes.GetMimeType(path); - return new FileStreamResult(System.IO.File.OpenRead(path), contentType); + return File(System.IO.File.OpenRead(path), contentType); } /// @@ -168,7 +168,7 @@ namespace Jellyfin.Api.Controllers.Images if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) { var contentType = MimeTypes.GetMimeType(path); - return new FileStreamResult(System.IO.File.OpenRead(path), contentType); + return File(System.IO.File.OpenRead(path), contentType); } } @@ -181,7 +181,7 @@ namespace Jellyfin.Api.Controllers.Images if (!string.IsNullOrEmpty(path) && System.IO.File.Exists(path)) { var contentType = MimeTypes.GetMimeType(path); - return new FileStreamResult(System.IO.File.OpenRead(path), contentType); + return File(System.IO.File.OpenRead(path), contentType); } }