using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Net.Mime;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers
{
///
/// Images By Name Controller.
///
[Route("Images")]
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.
///
/// Retrieved list of images.
/// An containing the list of images.
[HttpGet("General")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetGeneralImages()
{
return GetImageList(_applicationPaths.GeneralPath, false);
}
///
/// Get General Image.
///
/// The name of the image.
/// Image Type (primary, backdrop, logo, etc).
/// 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}")]
[AllowAnonymous]
[Produces(MediaTypeNames.Application.Octet)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesImageFile]
public ActionResult GetGeneralImage([FromRoute, Required] string name, [FromRoute, Required] string type)
{
var filename = string.Equals(type, "primary", StringComparison.OrdinalIgnoreCase)
? "folder"
: type;
var path = BaseItem.SupportedImageExtensions
.Select(i => Path.GetFullPath(Path.Combine(_applicationPaths.GeneralPath, name, filename + i)))
.FirstOrDefault(System.IO.File.Exists);
if (path == null)
{
return NotFound();
}
if (!path.StartsWith(_applicationPaths.GeneralPath))
{
return BadRequest("Invalid image path.");
}
var contentType = MimeTypes.GetMimeType(path);
return File(System.IO.File.OpenRead(path), contentType);
}
///
/// Get all general images.
///
/// Retrieved list of images.
/// An containing the list of images.
[HttpGet("Ratings")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetRatingImages()
{
return GetImageList(_applicationPaths.RatingsPath, false);
}
///
/// Get rating image.
///
/// The theme to get the image from.
/// The name of the image.
/// 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}")]
[AllowAnonymous]
[Produces(MediaTypeNames.Application.Octet)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesImageFile]
public ActionResult GetRatingImage(
[FromRoute, Required] string theme,
[FromRoute, Required] string name)
{
return GetImageFile(_applicationPaths.RatingsPath, theme, name);
}
///
/// Get all media info images.
///
/// Image list retrieved.
/// An containing the list of images.
[HttpGet("MediaInfo")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetMediaInfoImages()
{
return GetImageList(_applicationPaths.MediaInfoImagesPath, false);
}
///
/// Get media info image.
///
/// The theme to get the image from.
/// The name of the image.
/// 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}")]
[AllowAnonymous]
[Produces(MediaTypeNames.Application.Octet)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesImageFile]
public ActionResult GetMediaInfoImage(
[FromRoute, Required] string theme,
[FromRoute, Required] string name)
{
return GetImageFile(_applicationPaths.MediaInfoImagesPath, theme, name);
}
///
/// Internal FileHelper.
///
/// Path to begin search.
/// Theme to search.
/// File name to search for.
/// 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.GetFullPath(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))
{
if (!path.StartsWith(basePath))
{
return BadRequest("Invalid image path.");
}
var contentType = MimeTypes.GetMimeType(path);
return PhysicalFile(path, contentType);
}
}
var allFolder = Path.GetFullPath(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))
{
if (!path.StartsWith(basePath))
{
return BadRequest("Invalid image path.");
}
var contentType = MimeTypes.GetMimeType(path);
return PhysicalFile(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;
}
}
}