From e4088ba0bd1b10bca981a7e36e89fb2827e5dbe1 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sat, 18 Jun 2022 13:10:50 -0500 Subject: [PATCH 01/13] don't require a user id for items api call using api key --- Jellyfin.Api/Controllers/ItemsController.cs | 44 ++++++++++++++----- .../Controllers/TrailersController.cs | 7 +-- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 58caae9f85..7582c94cfe 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -9,6 +10,7 @@ using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -32,6 +34,7 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; @@ -42,6 +45,7 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public ItemsController( @@ -49,6 +53,7 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, + IAuthorizationContext authContext, ILogger logger, ISessionManager sessionManager) { @@ -56,6 +61,7 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _localization = localization; _dtoService = dtoService; + _authContext = authContext; _logger = logger; _sessionManager = sessionManager; } @@ -151,8 +157,8 @@ namespace Jellyfin.Api.Controllers /// A with the items. [HttpGet("Items")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetItems( - [FromQuery] Guid userId, + public async Task>> GetItems( + [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, [FromQuery] bool? hasThemeVideo, @@ -238,7 +244,17 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true) { - var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); + var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); + + var user = !auth.IsApiKey && userId.HasValue && !userId.Equals(Guid.Empty) + ? _userManager.GetUserById(userId.Value) + : null; + + if (!auth.IsApiKey && user is null) + { + return BadRequest("userId is required"); + } + var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(Request) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); @@ -270,20 +286,26 @@ namespace Jellyfin.Api.Controllers includeItemTypes = new[] { BaseItemKind.Playlist }; } - var enabledChannels = user!.GetPreferenceValues(PreferenceKind.EnabledChannels); + var enabledChannels = auth.IsApiKey + ? Array.Empty() + : user.GetPreferenceValues(PreferenceKind.EnabledChannels); - bool isInEnabledFolder = Array.IndexOf(user.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 + bool isInEnabledFolder = auth.IsApiKey + || Array.IndexOf(user.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.Id) != -1 // Assume all items inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.ChannelId) != -1; - var collectionFolders = _libraryManager.GetCollectionFolders(item); - foreach (var collectionFolder in collectionFolders) + if (!isInEnabledFolder) { - if (user.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) + var collectionFolders = _libraryManager.GetCollectionFolders(item); + foreach (var collectionFolder in collectionFolders) { - isInEnabledFolder = true; + if (user.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) + { + isInEnabledFolder = true; + } } } @@ -293,7 +315,7 @@ namespace Jellyfin.Api.Controllers && !user.HasPermission(PermissionKind.EnableAllChannels) && !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase)) { - _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name); + _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user.Username, item.Name); return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}."); } @@ -606,7 +628,7 @@ namespace Jellyfin.Api.Controllers /// A with the items. [HttpGet("Users/{userId}/Items")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetItemsByUserId( + public Task>> GetItemsByUserId( [FromRoute] Guid userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index 790d6e64d8..78a493d225 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; @@ -31,7 +32,7 @@ namespace Jellyfin.Api.Controllers /// /// Finds movies and trailers similar to a given trailer. /// - /// The user id. + /// Optional user id. /// Optional filter by maximum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items with theme songs. /// Optional filter by items with theme videos. @@ -118,8 +119,8 @@ namespace Jellyfin.Api.Controllers /// A with the trailers. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetTrailers( - [FromQuery] Guid userId, + public Task>> GetTrailers( + [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, [FromQuery] bool? hasThemeVideo, From 82df4c32427410515e7347613c34bcfe034db58f Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sat, 18 Jun 2022 13:15:05 -0500 Subject: [PATCH 02/13] update comments --- Jellyfin.Api/Controllers/ItemsController.cs | 2 +- Jellyfin.Api/Controllers/TrailersController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 7582c94cfe..b3e2beb0f9 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers /// /// Gets items based on a query. /// - /// The user id supplied as query parameter. + /// The user id supplied as query parameter; this is required when not using an API key. /// Optional filter by maximum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items with theme songs. /// Optional filter by items with theme videos. diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index 78a493d225..ea74c78736 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -32,7 +32,7 @@ namespace Jellyfin.Api.Controllers /// /// Finds movies and trailers similar to a given trailer. /// - /// Optional user id. + /// The user id supplied as query parameter; this is required when not using an API key. /// Optional filter by maximum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items with theme songs. /// Optional filter by items with theme videos. From d06fda43c18b8f600b15365b59d20b603fede141 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sat, 18 Jun 2022 13:19:00 -0500 Subject: [PATCH 03/13] use null-forgiving operator to suppress warnings --- Jellyfin.Api/Controllers/ItemsController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index b3e2beb0f9..0b1ef00224 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -288,10 +288,10 @@ namespace Jellyfin.Api.Controllers var enabledChannels = auth.IsApiKey ? Array.Empty() - : user.GetPreferenceValues(PreferenceKind.EnabledChannels); + : user!.GetPreferenceValues(PreferenceKind.EnabledChannels); bool isInEnabledFolder = auth.IsApiKey - || Array.IndexOf(user.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 + || Array.IndexOf(user!.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.Id) != -1 // Assume all items inside an EnabledChannel are enabled @@ -302,7 +302,7 @@ namespace Jellyfin.Api.Controllers var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { - if (user.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) + if (user!.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) { isInEnabledFolder = true; } @@ -311,7 +311,7 @@ namespace Jellyfin.Api.Controllers if (item is not UserRootFolder && !isInEnabledFolder - && !user.HasPermission(PermissionKind.EnableAllFolders) + && !user!.HasPermission(PermissionKind.EnableAllFolders) && !user.HasPermission(PermissionKind.EnableAllChannels) && !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase)) { From fc74c8eecf5bf0023ea59918260a4cdbe9ce787c Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Thu, 23 Jun 2022 09:19:29 -0500 Subject: [PATCH 04/13] tweak guid check Co-authored-by: Bond-009 --- Jellyfin.Api/Controllers/ItemsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 0b1ef00224..bfa4f9fe72 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -246,7 +246,7 @@ namespace Jellyfin.Api.Controllers { var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var user = !auth.IsApiKey && userId.HasValue && !userId.Equals(Guid.Empty) + var user = !auth.IsApiKey && userId.HasValue && !userId.Value.Equals(default) ? _userManager.GetUserById(userId.Value) : null; From 9ec42f8cf5cc4727f704f6a9b8e4e1c4aa8df04f Mon Sep 17 00:00:00 2001 From: Utku Ozdemir Date: Sun, 5 Jun 2022 02:38:22 +0200 Subject: [PATCH 05/13] fix: single video folder during photos lib scan Prevent a directory with a single video file to be detected as a movie directory when the library type is "Photos". Closes jellyfin/jellyfin#7825 Signed-off-by: Utku Ozdemir --- .../Library/Resolvers/Movies/MovieResolver.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index fe4ccd6acb..a60251dacd 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -464,7 +464,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies var result = ResolveVideos(parent, fileSystemEntries, SupportsMultiVersion, collectionType, parseName) ?? new MultiItemResolverResult(); - if (result.Items.Count == 1) + var isPhotosCollection = string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) + || string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase); + if (!isPhotosCollection && result.Items.Count == 1) { var videoPath = result.Items[0].Path; var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(videoPath, i.Name)); From d83b1c0f5a227303feb7b3c5af095cf0562d1795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Utku=20=C3=96zdemir?= Date: Sat, 11 Jun 2022 02:33:15 +0200 Subject: [PATCH 06/13] chore: add utkuozdemir to contributors.md Signed-off-by: Utku Ozdemir --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 87086a7280..a7d2b2e409 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -225,3 +225,4 @@ - [gnuyent](https://github.com/gnuyent) - [Matthew Jones](https://github.com/matthew-jones-uk) - [Jakob Kukla](https://github.com/jakobkukla) + - [Utku Özdemir](https://github.com/utkuozdemir) From c69b2c849ade5bcebed9ab79a020895285f8fdc6 Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sat, 30 Jul 2022 08:12:59 -0500 Subject: [PATCH 07/13] add comments --- Jellyfin.Api/Controllers/ItemsController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index bfa4f9fe72..a61b952f00 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -246,10 +246,12 @@ namespace Jellyfin.Api.Controllers { var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); + // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method var user = !auth.IsApiKey && userId.HasValue && !userId.Value.Equals(default) ? _userManager.GetUserById(userId.Value) : null; + // beyond this point, we're either using an api key or we have a valid user if (!auth.IsApiKey && user is null) { return BadRequest("userId is required"); @@ -290,6 +292,7 @@ namespace Jellyfin.Api.Controllers ? Array.Empty() : user!.GetPreferenceValues(PreferenceKind.EnabledChannels); + // api keys are always enabled for all folders bool isInEnabledFolder = auth.IsApiKey || Array.IndexOf(user!.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled @@ -302,6 +305,7 @@ namespace Jellyfin.Api.Controllers var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { + // api keys never enter this block, so user is never null if (user!.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) { isInEnabledFolder = true; @@ -309,6 +313,7 @@ namespace Jellyfin.Api.Controllers } } + // api keys are always enabled for all folders, so user is never null if (item is not UserRootFolder && !isInEnabledFolder && !user!.HasPermission(PermissionKind.EnableAllFolders) From 7391b001eff764d88fad8f35daffa1de0ca0960e Mon Sep 17 00:00:00 2001 From: MagicGreenDragon <14246920+MagicGreenDragon@users.noreply.github.com> Date: Sun, 14 Aug 2022 08:53:00 +0200 Subject: [PATCH 08/13] increased subtitle extraction timeout to 30 min --- CONTRIBUTORS.md | 1 + MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 87086a7280..fb582238df 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -157,6 +157,7 @@ - [jonas-resch](https://github.com/jonas-resch) - [vgambier](https://github.com/vgambier) - [MinecraftPlaye](https://github.com/MinecraftPlaye) + - [RealGreenDragon](https://github.com/RealGreenDragon) # Emby Contributors diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 50c4d92103..41354a0b90 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -575,7 +575,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw; } - var ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMinutes(5)).ConfigureAwait(false); + var ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMinutes(30)).ConfigureAwait(false); if (!ranToCompletion) { From 0f9124423941f7d2e00333942d3766c4cb8bac92 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sun, 14 Aug 2022 12:47:25 +0200 Subject: [PATCH 09/13] Use Guid for adjacentTo API parameter --- Jellyfin.Api/Controllers/ItemsController.cs | 4 ++-- Jellyfin.Api/Controllers/TrailersController.cs | 2 +- Jellyfin.Api/Controllers/TvShowsController.cs | 8 ++++---- MediaBrowser.Controller/Entities/Folder.cs | 6 +++--- .../Entities/InternalItemsQuery.cs | 2 +- MediaBrowser.Controller/Entities/UserViewBuilder.cs | 11 +++++------ 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 58caae9f85..1d207d9ad3 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -159,7 +159,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? hasSubtitles, [FromQuery] bool? hasSpecialFeature, [FromQuery] bool? hasTrailer, - [FromQuery] string? adjacentTo, + [FromQuery] Guid? adjacentTo, [FromQuery] int? parentIndexNumber, [FromQuery] bool? hasParentalRating, [FromQuery] bool? isHd, @@ -614,7 +614,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? hasSubtitles, [FromQuery] bool? hasSpecialFeature, [FromQuery] bool? hasTrailer, - [FromQuery] string? adjacentTo, + [FromQuery] Guid? adjacentTo, [FromQuery] int? parentIndexNumber, [FromQuery] bool? hasParentalRating, [FromQuery] bool? isHd, diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index 790d6e64d8..1c5aa9b8e3 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? hasSubtitles, [FromQuery] bool? hasSpecialFeature, [FromQuery] bool? hasTrailer, - [FromQuery] string? adjacentTo, + [FromQuery] Guid? adjacentTo, [FromQuery] int? parentIndexNumber, [FromQuery] bool? hasParentalRating, [FromQuery] bool? isHd, diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index 179a53fd54..a74538b008 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -206,7 +206,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? season, [FromQuery] Guid? seasonId, [FromQuery] bool? isMissing, - [FromQuery] string? adjacentTo, + [FromQuery] Guid? adjacentTo, [FromQuery] Guid? startItemId, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -278,9 +278,9 @@ namespace Jellyfin.Api.Controllers } // This must be the last filter - if (!string.IsNullOrEmpty(adjacentTo)) + if (adjacentTo.HasValue) { - episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo).ToList(); + episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo.Value).ToList(); } if (string.Equals(sortBy, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase)) @@ -326,7 +326,7 @@ namespace Jellyfin.Api.Controllers [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery] bool? isSpecialSeason, [FromQuery] bool? isMissing, - [FromQuery] string? adjacentTo, + [FromQuery] Guid? adjacentTo, [FromQuery] bool? enableImages, [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes, diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index b6983b73ee..7cab38a6c6 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -860,7 +860,7 @@ namespace MediaBrowser.Controller.Entities return true; } - if (!string.IsNullOrEmpty(query.AdjacentTo)) + if (query.AdjacentTo.HasValue) { Logger.LogDebug("Query requires post-filtering due to AdjacentTo"); return true; @@ -1029,9 +1029,9 @@ namespace MediaBrowser.Controller.Entities #pragma warning restore CA1309 // This must be the last filter - if (!string.IsNullOrEmpty(query.AdjacentTo)) + if (query.AdjacentTo.HasValue) { - items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo); + items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } return UserViewBuilder.SortAndPage(items, null, query, LibraryManager, enableSorting); diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index db1697c790..13bfd07c34 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -129,7 +129,7 @@ namespace MediaBrowser.Controller.Entities public Guid[] ExcludeItemIds { get; set; } - public string? AdjacentTo { get; set; } + public Guid? AdjacentTo { get; set; } public string[] PersonTypes { get; set; } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 2996104e70..3a6b7ac2c3 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -433,9 +433,9 @@ namespace MediaBrowser.Controller.Entities var user = query.User; // This must be the last filter - if (!string.IsNullOrEmpty(query.AdjacentTo)) + if (query.AdjacentTo.HasValue) { - items = FilterForAdjacency(items.ToList(), query.AdjacentTo); + items = FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } return SortAndPage(items, totalRecordLimit, query, libraryManager, true); @@ -985,10 +985,9 @@ namespace MediaBrowser.Controller.Entities return _userViewManager.GetUserSubView(parent.Id, type, localizationKey, sortName); } - public static IEnumerable FilterForAdjacency(List list, string adjacentToId) + public static IEnumerable FilterForAdjacency(List list, Guid adjacentTo) { - var adjacentToIdGuid = new Guid(adjacentToId); - var adjacentToItem = list.FirstOrDefault(i => i.Id.Equals(adjacentToIdGuid)); + var adjacentToItem = list.FirstOrDefault(i => i.Id.Equals(adjacentTo)); var index = list.IndexOf(adjacentToItem); @@ -1005,7 +1004,7 @@ namespace MediaBrowser.Controller.Entities nextId = list[index + 1].Id; } - return list.Where(i => i.Id.Equals(previousId) || i.Id.Equals(nextId) || i.Id.Equals(adjacentToIdGuid)); + return list.Where(i => i.Id.Equals(previousId) || i.Id.Equals(nextId) || i.Id.Equals(adjacentTo)); } } } From 61afd029dfbc3e3d7d14a980ce1682cfc8a04b94 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sun, 14 Aug 2022 12:58:38 +0200 Subject: [PATCH 10/13] Check for empty guid --- Jellyfin.Api/Controllers/TvShowsController.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 4 ++-- MediaBrowser.Controller/Entities/UserViewBuilder.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index a74538b008..fc376d0699 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -278,7 +278,7 @@ namespace Jellyfin.Api.Controllers } // This must be the last filter - if (adjacentTo.HasValue) + if (adjacentTo.HasValue && !adjacentTo.Value.Equals(default)) { episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo.Value).ToList(); } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 7cab38a6c6..1860da4c79 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -860,7 +860,7 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AdjacentTo.HasValue) + if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default)) { Logger.LogDebug("Query requires post-filtering due to AdjacentTo"); return true; @@ -1029,7 +1029,7 @@ namespace MediaBrowser.Controller.Entities #pragma warning restore CA1309 // This must be the last filter - if (query.AdjacentTo.HasValue) + if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default)) { items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 3a6b7ac2c3..f467a60384 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -433,7 +433,7 @@ namespace MediaBrowser.Controller.Entities var user = query.User; // This must be the last filter - if (query.AdjacentTo.HasValue) + if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default)) { items = FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } From bef4256d47c7424901bec0f7e977bae109917556 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sun, 14 Aug 2022 13:03:48 +0200 Subject: [PATCH 11/13] Use guid for seriesId API parameter --- Emby.Server.Implementations/TV/TVSeriesManager.cs | 10 +++++----- Jellyfin.Api/Controllers/TvShowsController.cs | 2 +- MediaBrowser.Model/Querying/NextUpQuery.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 727b9d4b5c..d7ab9c021e 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -43,9 +43,9 @@ namespace Emby.Server.Implementations.TV } string presentationUniqueKey = null; - if (!string.IsNullOrEmpty(query.SeriesId)) + if (query.SeriesId.HasValue && !query.SeriesId.Value.Equals(default)) { - if (_libraryManager.GetItemById(query.SeriesId) is Series series) + if (_libraryManager.GetItemById(query.SeriesId.Value) is Series series) { presentationUniqueKey = GetUniqueSeriesKey(series); } @@ -93,9 +93,9 @@ namespace Emby.Server.Implementations.TV string presentationUniqueKey = null; int? limit = null; - if (!string.IsNullOrEmpty(request.SeriesId)) + if (request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default)) { - if (_libraryManager.GetItemById(request.SeriesId) is Series series) + if (_libraryManager.GetItemById(request.SeriesId.Value) is Series series) { presentationUniqueKey = GetUniqueSeriesKey(series); limit = 1; @@ -153,7 +153,7 @@ namespace Emby.Server.Implementations.TV // If viewing all next up for all series, remove first episodes // But if that returns empty, keep those first episodes (avoid completely empty view) - var alwaysEnableFirstEpisode = !string.IsNullOrEmpty(request.SeriesId); + var alwaysEnableFirstEpisode = request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default); var anyFound = false; return allNextUp diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index 179a53fd54..fa732241b5 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -77,7 +77,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, - [FromQuery] string? seriesId, + [FromQuery] Guid? seriesId, [FromQuery] Guid? parentId, [FromQuery] bool? enableImages, [FromQuery] int? imageTypeLimit, diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs index 133d6a9162..0fb996df97 100644 --- a/MediaBrowser.Model/Querying/NextUpQuery.cs +++ b/MediaBrowser.Model/Querying/NextUpQuery.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Model.Querying /// Gets or sets the series id. /// /// The series id. - public string SeriesId { get; set; } + public Guid? SeriesId { get; set; } /// /// Gets or sets the start index. Use for paging. From de98457332a1e25d38553dd11403704372213e15 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Wed, 17 Aug 2022 20:14:07 +0200 Subject: [PATCH 12/13] Fix operation name in SearchController and correct nullability for SearchHint --- Jellyfin.Api/Controllers/SearchController.cs | 8 ++- MediaBrowser.Model/Search/SearchHint.cs | 66 +++++++++++++++----- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 07e113ad3e..138d8b8df4 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers [HttpGet] [Description("Gets search hints based on a search term")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult Get( + public ActionResult GetSearchHints( [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] Guid? userId, @@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers IndexNumber = item.IndexNumber, ParentIndexNumber = item.ParentIndexNumber, Id = item.Id, - Type = item.GetClientTypeName(), + Type = item.GetBaseItemKind(), MediaType = item.MediaType, MatchedTerm = hintInfo.MatchedTerm, RunTimeTicks = item.RunTimeTicks, @@ -149,8 +149,10 @@ namespace Jellyfin.Api.Controllers EndDate = item.EndDate }; - // legacy +#pragma warning disable CS0618 + // Kept for compatibility with older clients result.ItemId = result.Id; +#pragma warning restore CS0618 if (item.IsFolder) { diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs index 983dbd2bc0..4696c3797b 100644 --- a/MediaBrowser.Model/Search/SearchHint.cs +++ b/MediaBrowser.Model/Search/SearchHint.cs @@ -1,8 +1,6 @@ -#nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; +using Jellyfin.Data.Enums; namespace MediaBrowser.Model.Search { @@ -11,12 +9,28 @@ namespace MediaBrowser.Model.Search /// public class SearchHint { + /// + /// Initializes a new instance of the class. + /// + public SearchHint() + { + Name = string.Empty; + MatchedTerm = string.Empty; + MediaType = string.Empty; + Artists = Array.Empty(); + } + /// /// Gets or sets the item id. /// /// The item id. + [Obsolete("Use Id instead")] public Guid ItemId { get; set; } + /// + /// Gets or sets the item id. + /// + /// The item id. public Guid Id { get; set; } /// @@ -53,38 +67,42 @@ namespace MediaBrowser.Model.Search /// Gets or sets the image tag. /// /// The image tag. - public string PrimaryImageTag { get; set; } + public string? PrimaryImageTag { get; set; } /// /// Gets or sets the thumb image tag. /// /// The thumb image tag. - public string ThumbImageTag { get; set; } + public string? ThumbImageTag { get; set; } /// /// Gets or sets the thumb image item identifier. /// /// The thumb image item identifier. - public string ThumbImageItemId { get; set; } + public string? ThumbImageItemId { get; set; } /// /// Gets or sets the backdrop image tag. /// /// The backdrop image tag. - public string BackdropImageTag { get; set; } + public string? BackdropImageTag { get; set; } /// /// Gets or sets the backdrop image item identifier. /// /// The backdrop image item identifier. - public string BackdropImageItemId { get; set; } + public string? BackdropImageItemId { get; set; } /// /// Gets or sets the type. /// /// The type. - public string Type { get; set; } + public BaseItemKind Type { get; set; } + /// + /// Gets a value indicating whether this instance is folder. + /// + /// true if this instance is folder; otherwise, false. public bool? IsFolder { get; set; } /// @@ -99,31 +117,47 @@ namespace MediaBrowser.Model.Search /// The type of the media. public string MediaType { get; set; } + /// + /// Gets or sets the start date. + /// + /// The start date. public DateTime? StartDate { get; set; } + /// + /// Gets or sets the end date. + /// + /// The end date. public DateTime? EndDate { get; set; } /// /// Gets or sets the series. /// /// The series. - public string Series { get; set; } + public string? Series { get; set; } - public string Status { get; set; } + /// + /// Gets or sets the status. + /// + /// The status. + public string? Status { get; set; } /// /// Gets or sets the album. /// /// The album. - public string Album { get; set; } + public string? Album { get; set; } - public Guid AlbumId { get; set; } + /// + /// Gets or sets the album id. + /// + /// The album id. + public Guid? AlbumId { get; set; } /// /// Gets or sets the album artist. /// /// The album artist. - public string AlbumArtist { get; set; } + public string? AlbumArtist { get; set; } /// /// Gets or sets the artists. @@ -147,13 +181,13 @@ namespace MediaBrowser.Model.Search /// Gets or sets the channel identifier. /// /// The channel identifier. - public Guid ChannelId { get; set; } + public Guid? ChannelId { get; set; } /// /// Gets or sets the name of the channel. /// /// The name of the channel. - public string ChannelName { get; set; } + public string? ChannelName { get; set; } /// /// Gets or sets the primary image aspect ratio. From 893ac59668c3b3a117eab9a0582bd30ad82c9dd4 Mon Sep 17 00:00:00 2001 From: oncetemp Date: Fri, 19 Aug 2022 16:16:40 +0000 Subject: [PATCH 13/13] Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 50d019f906..186ec44d24 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -120,5 +120,8 @@ "Forced": "강제하기", "Default": "기본 설정", "TaskOptimizeDatabaseDescription": "데이터베이스를 압축하고 사용 가능한 공간을 늘립니다. 라이브러리를 검색한 후 이 작업을 실행하거나 데이터베이스 수정같은 비슷한 작업을 수행하면 성능이 향상될 수 있습니다.", - "TaskOptimizeDatabase": "데이터베이스 최적화" + "TaskOptimizeDatabase": "데이터베이스 최적화", + "TaskKeyframeExtractorDescription": "비디오 파일에서 키프레임을 추출하여 더 정확한 HLS 재생 목록을 만듭니다. 이 작업은 오랫동안 진행될 수 있습니다.", + "TaskKeyframeExtractor": "키프레임 추출", + "External": "외부" }