diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index bb5cc746e9..0a4432beca 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -46,6 +46,7 @@ using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; +using TMDbLib.Objects.Authentication; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using EpisodeInfo = Emby.Naming.TV.EpisodeInfo; using Genre = MediaBrowser.Controller.Entities.Genre; @@ -1222,12 +1223,7 @@ namespace Emby.Server.Implementations.Library return null; } - /// - /// Gets the item by id. - /// - /// The id. - /// BaseItem. - /// is null. + /// public BaseItem GetItemById(Guid id) { if (id.IsEmpty()) @@ -1263,6 +1259,22 @@ namespace Emby.Server.Implementations.Library return null; } + /// + public T GetItemById(Guid id, Guid userId) + where T : BaseItem + { + var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId); + return GetItemById(id, user); + } + + /// + public T GetItemById(Guid id, User user) + where T : BaseItem + { + var item = GetItemById(id); + return ItemIsVisible(item, user) ? item : null; + } + public List GetItemList(InternalItemsQuery query, bool allowExternalContent) { if (query.Recursive && !query.ParentId.IsEmpty()) @@ -3191,5 +3203,20 @@ namespace Emby.Server.Implementations.Library CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); } + + private static bool ItemIsVisible(BaseItem item, User user) + { + if (item is null) + { + return false; + } + + if (user is null) + { + return true; + } + + return item is UserRootFolder || item.IsVisibleStandalone(user); + } } } diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index 1cad663264..6d94d96f3a 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -194,7 +194,7 @@ public class DisplayPreferencesController : BaseJellyfinApiController foreach (var key in displayPreferences.CustomPrefs.Keys.Where(key => key.StartsWith("landing-", StringComparison.OrdinalIgnoreCase))) { - if (!Enum.TryParse(displayPreferences.CustomPrefs[key], true, out var type)) + if (!Enum.TryParse(displayPreferences.CustomPrefs[key], true, out _)) { _logger.LogError("Invalid ViewType: {LandingScreenOption}", displayPreferences.CustomPrefs[key]); displayPreferences.CustomPrefs.Remove(key); diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index d6e043e6a1..4abca32713 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -162,7 +162,7 @@ public class FilterController : BaseJellyfinApiController } else if (parentId.HasValue) { - parentItem = _libraryManager.GetItemById(parentId.Value); + parentItem = _libraryManager.GetItemById(parentId.Value); } var filters = new QueryFilters(); diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 6b38fa7d34..8e8accab3c 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -90,6 +90,7 @@ public class ImageController : BaseJellyfinApiController /// User Id. /// Image updated. /// User does not have permission to delete the image. + /// Item not found. /// A . [HttpPost("UserImage")] [Authorize] @@ -97,6 +98,7 @@ public class ImageController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task PostUserImage( [FromQuery] Guid? userId) { @@ -289,7 +291,7 @@ public class ImageController : BaseJellyfinApiController [FromRoute, Required] ImageType imageType, [FromQuery] int? imageIndex) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -317,7 +319,7 @@ public class ImageController : BaseJellyfinApiController [FromRoute, Required] ImageType imageType, [FromRoute] int imageIndex) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -346,7 +348,7 @@ public class ImageController : BaseJellyfinApiController [FromRoute, Required] Guid itemId, [FromRoute, Required] ImageType imageType) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -390,7 +392,7 @@ public class ImageController : BaseJellyfinApiController [FromRoute, Required] ImageType imageType, [FromRoute] int imageIndex) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -433,7 +435,7 @@ public class ImageController : BaseJellyfinApiController [FromRoute, Required] int imageIndex, [FromQuery, Required] int newIndex) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -456,7 +458,7 @@ public class ImageController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetItemImageInfos([FromRoute, Required] Guid itemId) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -559,7 +561,7 @@ public class ImageController : BaseJellyfinApiController [FromQuery] string? foregroundLayer, [FromQuery] int? imageIndex) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -637,7 +639,7 @@ public class ImageController : BaseJellyfinApiController [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -715,7 +717,7 @@ public class ImageController : BaseJellyfinApiController [FromQuery] string? foregroundLayer, [FromRoute, Required] int imageIndex) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 3cf4852995..dcbacf1d78 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -62,9 +62,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("Songs/{itemId}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetInstantMixFromSong( [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, @@ -75,11 +77,16 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { - var item = _libraryManager.GetItemById(itemId); userId = RequestHelpers.GetUserId(User, userId); var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) + { + return NotFound(); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); @@ -99,9 +106,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("Albums/{itemId}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetInstantMixFromAlbum( [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, @@ -112,15 +121,20 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { - var album = _libraryManager.GetItemById(itemId); userId = RequestHelpers.GetUserId(User, userId); var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) + { + return NotFound(); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - var items = _musicManager.GetInstantMixFromItem(album, user, dtoOptions); + var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); } @@ -136,9 +150,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("Playlists/{itemId}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetInstantMixFromPlaylist( [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, @@ -149,15 +165,20 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { - var playlist = (Playlist)_libraryManager.GetItemById(itemId); userId = RequestHelpers.GetUserId(User, userId); var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) + { + return NotFound(); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - var items = _musicManager.GetInstantMixFromItem(playlist, user, dtoOptions); + var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); } @@ -209,9 +230,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("Artists/{itemId}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetInstantMixFromArtists( [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, @@ -222,11 +245,16 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { - var item = _libraryManager.GetItemById(itemId); userId = RequestHelpers.GetUserId(User, userId); var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) + { + return NotFound(); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); @@ -246,9 +274,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("Items/{itemId}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetInstantMixFromItem( [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, @@ -259,11 +289,16 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { - var item = _libraryManager.GetItemById(itemId); userId = RequestHelpers.GetUserId(User, userId); var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) + { + return NotFound(); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); @@ -283,9 +318,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("Artists/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] [Obsolete("Use GetInstantMixFromArtists")] public ActionResult> GetInstantMixFromArtists2( [FromQuery, Required] Guid id, @@ -320,9 +357,11 @@ public class InstantMixController : BaseJellyfinApiController /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Instant playlist returned. + /// Item not found. /// A with the playlist items. [HttpGet("MusicGenres/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetInstantMixFromMusicGenreById( [FromQuery, Required] Guid id, [FromQuery] Guid? userId, @@ -333,11 +372,16 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { - var item = _libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(id, user); + if (item is null) + { + return NotFound(); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index e3aee1bf7a..d009f80a96 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -4,6 +4,8 @@ using System.ComponentModel.DataAnnotations; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using MediaBrowser.Common.Api; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -64,7 +66,7 @@ public class ItemLookupController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetExternalIdInfos([FromRoute, Required] Guid itemId) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -234,6 +236,7 @@ public class ItemLookupController : BaseJellyfinApiController /// The remote search result. /// Optional. Whether or not to replace all images. Default: True. /// Item metadata refreshed. + /// Item not found. /// /// A that represents the asynchronous operation to get the remote search results. /// The task result contains an . @@ -241,12 +244,18 @@ public class ItemLookupController : BaseJellyfinApiController [HttpPost("Items/RemoteSearch/Apply/{itemId}")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task ApplySearchCriteria( [FromRoute, Required] Guid itemId, [FromBody, Required] RemoteSearchResult searchResult, [FromQuery] bool replaceAllImages = true) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); + if (item is null) + { + return NotFound(); + } + _logger.LogInformation( "Setting provider id's to item {ItemId}-{ItemName}: {@ProviderIds}", item.Id, diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs index 0a8522e1cf..c1343b1309 100644 --- a/Jellyfin.Api/Controllers/ItemRefreshController.cs +++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs @@ -2,7 +2,10 @@ using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using MediaBrowser.Common.Api; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.IO; @@ -61,7 +64,7 @@ public class ItemRefreshController : BaseJellyfinApiController [FromQuery] bool replaceAllMetadata = false, [FromQuery] bool replaceAllImages = false) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 9800248c68..83f308bb19 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; using MediaBrowser.Common.Api; using MediaBrowser.Controller.Configuration; @@ -72,7 +74,7 @@ public class ItemUpdateController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateItem([FromRoute, Required] Guid itemId, [FromBody, Required] BaseItemDto request) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -145,7 +147,11 @@ public class ItemUpdateController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetMetadataEditorInfo([FromRoute, Required] Guid itemId) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); + if (item is null) + { + return NotFound(); + } var info = new MetadataEditorInfo { @@ -197,7 +203,7 @@ public class ItemUpdateController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery] string? contentType) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 26ae1a820f..6ffe6e7da1 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -967,9 +967,13 @@ public class ItemsController : BaseJellyfinApiController } var user = _userManager.GetUserById(requestUserId) ?? throw new ResourceNotFoundException(); - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) + { + return NotFound(); + } - return (item == null) ? NotFound() : _userDataRepository.GetUserDataDto(item, user); + return _userDataRepository.GetUserDataDto(item, user); } /// @@ -1014,8 +1018,8 @@ public class ItemsController : BaseJellyfinApiController } var user = _userManager.GetUserById(requestUserId) ?? throw new ResourceNotFoundException(); - var item = _libraryManager.GetItemById(itemId); - if (item == null) + var item = _libraryManager.GetItemById(itemId, user); + if (item is null) { return NotFound(); } diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 360389d292..3b4e80ff3c 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -102,7 +102,7 @@ public class LibraryController : BaseJellyfinApiController [ProducesFile("video/*", "audio/*")] public ActionResult GetFile([FromRoute, Required] Guid itemId) { - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, User.GetUserId()); if (item is null) { return NotFound(); @@ -152,11 +152,10 @@ public class LibraryController : BaseJellyfinApiController ? (userId.IsNullOrEmpty() ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) - : _libraryManager.GetItemById(itemId); - + : _libraryManager.GetItemById(itemId, user); if (item is null) { - return NotFound("Item not found."); + return NotFound(); } IEnumerable themeItems; @@ -214,16 +213,14 @@ public class LibraryController : BaseJellyfinApiController var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = itemId.IsEmpty() ? (userId.IsNullOrEmpty() ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) - : _libraryManager.GetItemById(itemId); - + : _libraryManager.GetItemById(itemId, user); if (item is null) { - return NotFound("Item not found."); + return NotFound(); } IEnumerable themeItems; @@ -286,7 +283,8 @@ public class LibraryController : BaseJellyfinApiController userId, inheritFromParent); - if (themeSongs.Result is NotFoundObjectResult || themeVideos.Result is NotFoundObjectResult) + if (themeSongs.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound } + || themeVideos.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound }) { return NotFound(); } @@ -327,6 +325,7 @@ public class LibraryController : BaseJellyfinApiController /// The item id. /// Item deleted. /// Unauthorized access. + /// Item not found. /// A . [HttpDelete("Items/{itemId}")] [Authorize] @@ -335,17 +334,18 @@ public class LibraryController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult DeleteItem(Guid itemId) { - var isApiKey = User.GetIsApiKey(); var userId = User.GetUserId(); - var user = !isApiKey && !userId.IsEmpty() - ? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException() - : null; - if (!isApiKey && user is null) + var isApiKey = User.GetIsApiKey(); + var user = userId.IsEmpty() && isApiKey + ? null + : _userManager.GetUserById(userId); + + if (user is null && !isApiKey) { - return Unauthorized("Unauthorized access"); + return NotFound(); } - var item = _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId, user); if (item is null) { return NotFound(); @@ -391,7 +391,7 @@ public class LibraryController : BaseJellyfinApiController foreach (var i in ids) { - var item = _libraryManager.GetItemById(i); + var item = _libraryManager.GetItemById(i, user); if (item is null) { return NotFound(); @@ -459,20 +459,18 @@ public class LibraryController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { - var item = _libraryManager.GetItemById(itemId); userId = RequestHelpers.GetUserId(User, userId); - + var user = userId.IsNullOrEmpty() + ? null + : _userManager.GetUserById(userId.Value); + var item = _libraryManager.GetItemById(itemId, user); if (item is null) { - return NotFound("Item not found"); + return NotFound(); } var baseItemDtos = new List(); - var user = userId.IsNullOrEmpty() - ? null - : _userManager.GetUserById(userId.Value); - var dtoOptions = new DtoOptions().AddClientFields(User); BaseItem? parent = item.GetParent(); @@ -644,14 +642,16 @@ public class LibraryController : BaseJellyfinApiController [ProducesFile("video/*", "audio/*")] public async Task GetDownload([FromRoute, Required] Guid itemId) { - var item = _libraryManager.GetItemById(itemId); + var userId = User.GetUserId(); + var user = userId.IsEmpty() + ? null + : _userManager.GetUserById(userId); + var item = _libraryManager.GetItemById(itemId, user); if (item is null) { return NotFound(); } - var user = _userManager.GetUserById(User.GetUserId()); - if (user is not null) { if (!item.CanDownload(user)) @@ -704,12 +704,14 @@ public class LibraryController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields) { userId = RequestHelpers.GetUserId(User, userId); + var user = userId.IsNullOrEmpty() + ? null + : _userManager.GetUserById(userId.Value); var item = itemId.IsEmpty() - ? (userId.IsNullOrEmpty() + ? (user is null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) - : _libraryManager.GetItemById(itemId); - + : _libraryManager.GetItemById(itemId, user); if (item is null) { return NotFound(); @@ -720,9 +722,6 @@ public class LibraryController : BaseJellyfinApiController return new QueryResult(); } - var user = userId.IsNullOrEmpty() - ? null - : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User); diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs index 23c430f859..c1d01a5c2f 100644 --- a/Jellyfin.Api/Controllers/LibraryStructureController.cs +++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs @@ -6,6 +6,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.LibraryStructureDto; using MediaBrowser.Common.Api; @@ -311,15 +313,21 @@ public class LibraryStructureController : BaseJellyfinApiController /// /// The library name and options. /// Library updated. + /// Item not found. /// A . [HttpPost("LibraryOptions")] [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateLibraryOptions( [FromBody] UpdateLibraryOptionsDto request) { - var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id); + var item = _libraryManager.GetItemById(request.Id, User.GetUserId()); + if (item is null) + { + return NotFound(); + } - collectionFolder.UpdateLibraryOptions(request.LibraryOptions); + item.UpdateLibraryOptions(request.LibraryOptions); return NoContent(); } } diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 7768b3c45f..2b26c01f88 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -220,9 +220,11 @@ public class LiveTvController : BaseJellyfinApiController /// Channel id. /// Optional. Attach user data. /// Live tv channel returned. + /// Item not found. /// An containing the live tv channel. [HttpGet("Channels/{channelId}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(Policy = Policies.LiveTvAccess)] public ActionResult GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId) { @@ -232,7 +234,12 @@ public class LiveTvController : BaseJellyfinApiController : _userManager.GetUserById(userId.Value); var item = channelId.IsEmpty() ? _libraryManager.GetUserRootFolder() - : _libraryManager.GetItemById(channelId); + : _libraryManager.GetItemById(channelId, user); + + if (item is null) + { + return NotFound(); + } var dtoOptions = new DtoOptions() .AddClientFields(User); @@ -416,9 +423,11 @@ public class LiveTvController : BaseJellyfinApiController /// Recording id. /// Optional. Attach user data. /// Recording returned. + /// Item not found. /// An containing the live tv recording. [HttpGet("Recordings/{recordingId}")] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(Policy = Policies.LiveTvAccess)] public ActionResult GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId) { @@ -426,7 +435,13 @@ public class LiveTvController : BaseJellyfinApiController var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = recordingId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId); + var item = recordingId.IsEmpty() + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(recordingId, user); + if (item is null) + { + return NotFound(); + } var dtoOptions = new DtoOptions() .AddClientFields(User); @@ -611,7 +626,8 @@ public class LiveTvController : BaseJellyfinApiController { query.IsSeries = true; - if (_libraryManager.GetItemById(librarySeriesId.Value) is Series series) + var series = _libraryManager.GetItemById(librarySeriesId.Value); + if (series is not null) { query.Name = series.Name; } @@ -665,7 +681,8 @@ public class LiveTvController : BaseJellyfinApiController { query.IsSeries = true; - if (_libraryManager.GetItemById(body.LibrarySeriesId) is Series series) + var series = _libraryManager.GetItemById(body.LibrarySeriesId); + if (series is not null) { query.Name = series.Name; } @@ -779,7 +796,7 @@ public class LiveTvController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult DeleteRecording([FromRoute, Required] Guid recordingId) { - var item = _libraryManager.GetItemById(recordingId); + var item = _libraryManager.GetItemById(recordingId, User.GetUserId()); if (item is null) { return NotFound(); diff --git a/Jellyfin.Api/Controllers/LyricsController.cs b/Jellyfin.Api/Controllers/LyricsController.cs index f2b312b478..8eb4cadf88 100644 --- a/Jellyfin.Api/Controllers/LyricsController.cs +++ b/Jellyfin.Api/Controllers/LyricsController.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using Jellyfin.Extensions; using MediaBrowser.Common.Api; using MediaBrowser.Controller.Entities.Audio; @@ -66,37 +67,16 @@ public class LyricsController : BaseJellyfinApiController [HttpGet("Audio/{itemId}/Lyrics")] [Authorize] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetLyrics([FromRoute, Required] Guid itemId) { - var isApiKey = User.GetIsApiKey(); - var userId = User.GetUserId(); - if (!isApiKey && userId.IsEmpty()) - { - return BadRequest(); - } - - var audio = _libraryManager.GetItemById