diff options
Diffstat (limited to 'Jellyfin.Api/Controllers')
| -rw-r--r-- | Jellyfin.Api/Controllers/FilterController.cs | 8 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/ImageByNameController.cs | 252 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/LibraryController.cs | 15 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/MoviesController.cs | 6 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/NotificationsController.cs | 87 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/UserController.cs | 10 |
6 files changed, 14 insertions, 364 deletions
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index b6780ee20..17d136384 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -92,25 +92,25 @@ namespace Jellyfin.Api.Controllers Years = itemList.Select(i => i.ProductionYear ?? -1) .Where(i => i > 0) .Distinct() - .OrderBy(i => i) + .Order() .ToArray(), Genres = itemList.SelectMany(i => i.Genres) .DistinctNames() - .OrderBy(i => i) + .Order() .ToArray(), Tags = itemList .SelectMany(i => i.Tags) .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) + .Order() .ToArray(), OfficialRatings = itemList .Select(i => i.OfficialRating) .Where(i => !string.IsNullOrWhiteSpace(i)) .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) + .Order() .ToArray() }; } diff --git a/Jellyfin.Api/Controllers/ImageByNameController.cs b/Jellyfin.Api/Controllers/ImageByNameController.cs deleted file mode 100644 index c54851b96..000000000 --- a/Jellyfin.Api/Controllers/ImageByNameController.cs +++ /dev/null @@ -1,252 +0,0 @@ -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 -{ - /// <summary> - /// Images By Name Controller. - /// </summary> - [Route("Images")] - public class ImageByNameController : BaseJellyfinApiController - { - private readonly IServerApplicationPaths _applicationPaths; - private readonly IFileSystem _fileSystem; - - /// <summary> - /// Initializes a new instance of the <see cref="ImageByNameController" /> class. - /// </summary> - /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager" /> interface.</param> - /// <param name="fileSystem">Instance of the <see cref="IFileSystem" /> interface.</param> - public ImageByNameController( - IServerConfigurationManager serverConfigurationManager, - IFileSystem fileSystem) - { - _applicationPaths = serverConfigurationManager.ApplicationPaths; - _fileSystem = fileSystem; - } - - /// <summary> - /// Get all general images. - /// </summary> - /// <response code="200">Retrieved list of images.</response> - /// <returns>An <see cref="OkResult"/> containing the list of images.</returns> - [HttpGet("General")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<IEnumerable<ImageByNameInfo>> GetGeneralImages() - { - return GetImageList(_applicationPaths.GeneralPath, false); - } - - /// <summary> - /// Get General Image. - /// </summary> - /// <param name="name">The name of the image.</param> - /// <param name="type">Image Type (primary, backdrop, logo, etc).</param> - /// <response code="200">Image stream retrieved.</response> - /// <response code="404">Image not found.</response> - /// <returns>A <see cref="FileStreamResult"/> containing the image contents on success, or a <see cref="NotFoundResult"/> if the image could not be found.</returns> - [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 is null) - { - return NotFound(); - } - - if (!path.StartsWith(_applicationPaths.GeneralPath, StringComparison.InvariantCulture)) - { - return BadRequest("Invalid image path."); - } - - var contentType = MimeTypes.GetMimeType(path); - return File(AsyncFile.OpenRead(path), contentType); - } - - /// <summary> - /// Get all general images. - /// </summary> - /// <response code="200">Retrieved list of images.</response> - /// <returns>An <see cref="OkResult"/> containing the list of images.</returns> - [HttpGet("Ratings")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<IEnumerable<ImageByNameInfo>> GetRatingImages() - { - return GetImageList(_applicationPaths.RatingsPath, false); - } - - /// <summary> - /// Get rating image. - /// </summary> - /// <param name="theme">The theme to get the image from.</param> - /// <param name="name">The name of the image.</param> - /// <response code="200">Image stream retrieved.</response> - /// <response code="404">Image not found.</response> - /// <returns>A <see cref="FileStreamResult"/> containing the image contents on success, or a <see cref="NotFoundResult"/> if the image could not be found.</returns> - [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); - } - - /// <summary> - /// Get all media info images. - /// </summary> - /// <response code="200">Image list retrieved.</response> - /// <returns>An <see cref="OkResult"/> containing the list of images.</returns> - [HttpGet("MediaInfo")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<IEnumerable<ImageByNameInfo>> GetMediaInfoImages() - { - return GetImageList(_applicationPaths.MediaInfoImagesPath, false); - } - - /// <summary> - /// Get media info image. - /// </summary> - /// <param name="theme">The theme to get the image from.</param> - /// <param name="name">The name of the image.</param> - /// <response code="200">Image stream retrieved.</response> - /// <response code="404">Image not found.</response> - /// <returns>A <see cref="FileStreamResult"/> containing the image contents on success, or a <see cref="NotFoundResult"/> if the image could not be found.</returns> - [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); - } - - /// <summary> - /// Internal FileHelper. - /// </summary> - /// <param name="basePath">Path to begin search.</param> - /// <param name="theme">Theme to search.</param> - /// <param name="name">File name to search for.</param> - /// <returns>A <see cref="FileStreamResult"/> containing the image contents on success, or a <see cref="NotFoundResult"/> if the image could not be found.</returns> - 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, StringComparison.InvariantCulture)) - { - 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, StringComparison.InvariantCulture)) - { - return BadRequest("Invalid image path."); - } - - var contentType = MimeTypes.GetMimeType(path); - return PhysicalFile(path, contentType); - } - } - - return NotFound(); - } - - private List<ImageByNameInfo> 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<ImageByNameInfo>(); - } - } - - 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/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index ab2020830..196d509fb 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -770,8 +770,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary) }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); result.MetadataReaders = plugins @@ -781,8 +780,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = true }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); result.SubtitleFetchers = plugins @@ -792,8 +790,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = true }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); var typeOptions = new List<LibraryTypeOptionsDto>(); @@ -814,8 +811,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary) }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(), ImageFetchers = plugins @@ -826,8 +822,7 @@ namespace Jellyfin.Api.Controllers Name = i.Name, DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary) }) - .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) - .Select(x => x.First()) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(), SupportedImageTypes = plugins diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 03f864b4a..3cf079362 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -200,8 +200,7 @@ namespace Jellyfin.Api.Controllers IsMovie = true, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) - .Select(x => x.First()) + }).DistinctBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Take(itemLimit) .ToList(); @@ -240,8 +239,7 @@ namespace Jellyfin.Api.Controllers IsMovie = true, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) - .Select(x => x.First()) + }).DistinctBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Take(itemLimit) .ToList(); diff --git a/Jellyfin.Api/Controllers/NotificationsController.cs b/Jellyfin.Api/Controllers/NotificationsController.cs index 420630cdf..a28556476 100644 --- a/Jellyfin.Api/Controllers/NotificationsController.cs +++ b/Jellyfin.Api/Controllers/NotificationsController.cs @@ -1,12 +1,5 @@ -using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading; using Jellyfin.Api.Constants; -using Jellyfin.Api.Models.NotificationDtos; -using Jellyfin.Data.Enums; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Notifications; @@ -23,41 +16,14 @@ namespace Jellyfin.Api.Controllers public class NotificationsController : BaseJellyfinApiController { private readonly INotificationManager _notificationManager; - private readonly IUserManager _userManager; /// <summary> /// Initializes a new instance of the <see cref="NotificationsController" /> class. /// </summary> /// <param name="notificationManager">The notification manager.</param> - /// <param name="userManager">The user manager.</param> - public NotificationsController(INotificationManager notificationManager, IUserManager userManager) + public NotificationsController(INotificationManager notificationManager) { _notificationManager = notificationManager; - _userManager = userManager; - } - - /// <summary> - /// Gets a user's notifications. - /// </summary> - /// <response code="200">Notifications returned.</response> - /// <returns>An <see cref="OkResult"/> containing a list of notifications.</returns> - [HttpGet("{userId}")] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<NotificationResultDto> GetNotifications() - { - return new NotificationResultDto(); - } - - /// <summary> - /// Gets a user's notification summary. - /// </summary> - /// <response code="200">Summary of user's notifications returned.</response> - /// <returns>An <cref see="OkResult"/> containing a summary of the users notifications.</returns> - [HttpGet("{userId}/Summary")] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<NotificationsSummaryDto> GetNotificationsSummary() - { - return new NotificationsSummaryDto(); } /// <summary> @@ -83,56 +49,5 @@ namespace Jellyfin.Api.Controllers { return _notificationManager.GetNotificationServices(); } - - /// <summary> - /// Sends a notification to all admins. - /// </summary> - /// <param name="notificationDto">The notification request.</param> - /// <response code="204">Notification sent.</response> - /// <returns>A <cref see="NoContentResult"/>.</returns> - [HttpPost("Admin")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult CreateAdminNotification([FromBody, Required] AdminNotificationDto notificationDto) - { - var notification = new NotificationRequest - { - Name = notificationDto.Name, - Description = notificationDto.Description, - Url = notificationDto.Url, - Level = notificationDto.NotificationLevel ?? NotificationLevel.Normal, - UserIds = _userManager.Users - .Where(user => user.HasPermission(PermissionKind.IsAdministrator)) - .Select(user => user.Id) - .ToArray(), - Date = DateTime.UtcNow, - }; - - _notificationManager.SendNotification(notification, CancellationToken.None); - return NoContent(); - } - - /// <summary> - /// Sets notifications as read. - /// </summary> - /// <response code="204">Notifications set as read.</response> - /// <returns>A <cref see="NoContentResult"/>.</returns> - [HttpPost("{userId}/Read")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult SetRead() - { - return NoContent(); - } - - /// <summary> - /// Sets notifications as unread. - /// </summary> - /// <response code="204">Notifications set as unread.</response> - /// <returns>A <cref see="NoContentResult"/>.</returns> - [HttpPost("{userId}/Unread")] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult SetUnread() - { - return NoContent(); - } } } diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 002327d74..568224a42 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -157,7 +157,6 @@ namespace Jellyfin.Api.Controllers /// </summary> /// <param name="userId">The user id.</param> /// <param name="pw">The password as plain text.</param> - /// <param name="password">The password sha1-hash.</param> /// <response code="200">User authenticated.</response> /// <response code="403">Sha1-hashed password only is not allowed.</response> /// <response code="404">User not found.</response> @@ -166,10 +165,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] + [Obsolete("Authenticate with username instead")] public async Task<ActionResult<AuthenticationResult>> AuthenticateUser( [FromRoute, Required] Guid userId, - [FromQuery, Required] string pw, - [FromQuery] string? password) + [FromQuery, Required] string pw) { var user = _userManager.GetUserById(userId); @@ -178,11 +177,6 @@ namespace Jellyfin.Api.Controllers return NotFound("User not found"); } - if (!string.IsNullOrEmpty(password) && string.IsNullOrEmpty(pw)) - { - return StatusCode(StatusCodes.Status403Forbidden, "Only sha1 password is not allowed."); - } - AuthenticateUserByName request = new AuthenticateUserByName { Username = user.Username, |
