diff options
Diffstat (limited to 'Jellyfin.Api')
| -rw-r--r-- | Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs | 18 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/ItemUpdateController.cs | 52 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/ItemsController.cs | 4 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/LibraryStructureController.cs | 18 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/TrailersController.cs | 1 | ||||
| -rw-r--r-- | Jellyfin.Api/Controllers/TvShowsController.cs | 7 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/MediaInfoHelper.cs | 13 | ||||
| -rw-r--r-- | Jellyfin.Api/Helpers/StreamingHelpers.cs | 14 |
8 files changed, 96 insertions, 31 deletions
diff --git a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs index 9b4e2182c..e425000cd 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using MediaBrowser.Common.Configuration; using Microsoft.AspNetCore.Authorization; @@ -24,24 +25,31 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy /// <inheritdoc /> protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, FirstTimeSetupRequirement requirement) { + // Succeed if the startup wizard / first time setup is not complete if (!_configurationManager.CommonConfiguration.IsStartupWizardCompleted) { context.Succeed(requirement); } - else if (requirement.RequireAdmin && !context.User.IsInRole(UserRoles.Administrator)) + + // Succeed if user is admin + else if (context.User.IsInRole(UserRoles.Administrator)) { - context.Fail(); + context.Succeed(requirement); } - else if (!requirement.RequireAdmin && context.User.IsInRole(UserRoles.Guest)) + + // Fail if admin is required and user is not admin + else if (requirement.RequireAdmin) { context.Fail(); } - else + + // Succeed if admin is not required and user is not guest + else if (context.User.IsInRole(UserRoles.User)) { - // Any user-specific checks are handled in the DefaultAuthorizationHandler. context.Succeed(requirement); } + // Any user-specific checks are handled in the DefaultAuthorizationHandler. return Task.CompletedTask; } } diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index b4ce343be..4001a6add 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -290,17 +290,35 @@ public class ItemUpdateController : BaseJellyfinApiController { foreach (var season in rseries.Children.OfType<Season>()) { - season.OfficialRating = request.OfficialRating; + if (!season.LockedFields.Contains(MetadataField.OfficialRating)) + { + season.OfficialRating = request.OfficialRating; + } + season.CustomRating = request.CustomRating; - season.Tags = season.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + + if (!season.LockedFields.Contains(MetadataField.Tags)) + { + season.Tags = season.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + } + season.OnMetadataChanged(); await season.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); foreach (var ep in season.Children.OfType<Episode>()) { - ep.OfficialRating = request.OfficialRating; + if (!ep.LockedFields.Contains(MetadataField.OfficialRating)) + { + ep.OfficialRating = request.OfficialRating; + } + ep.CustomRating = request.CustomRating; - ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + + if (!ep.LockedFields.Contains(MetadataField.Tags)) + { + ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + } + ep.OnMetadataChanged(); await ep.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } @@ -310,9 +328,18 @@ public class ItemUpdateController : BaseJellyfinApiController { foreach (var ep in season.Children.OfType<Episode>()) { - ep.OfficialRating = request.OfficialRating; + if (!ep.LockedFields.Contains(MetadataField.OfficialRating)) + { + ep.OfficialRating = request.OfficialRating; + } + ep.CustomRating = request.CustomRating; - ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + + if (!ep.LockedFields.Contains(MetadataField.Tags)) + { + ep.Tags = ep.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + } + ep.OnMetadataChanged(); await ep.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } @@ -321,9 +348,18 @@ public class ItemUpdateController : BaseJellyfinApiController { foreach (BaseItem track in album.Children) { - track.OfficialRating = request.OfficialRating; + if (!track.LockedFields.Contains(MetadataField.OfficialRating)) + { + track.OfficialRating = request.OfficialRating; + } + track.CustomRating = request.CustomRating; - track.Tags = track.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + + if (!track.LockedFields.Contains(MetadataField.Tags)) + { + track.Tags = track.Tags.Concat(addedTags).Except(removedTags).Distinct().ToArray(); + } + track.OnMetadataChanged(); await track.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); } diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index cd4a0a23b..d33634412 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -76,6 +76,7 @@ public class ItemsController : BaseJellyfinApiController /// <param name="hasSpecialFeature">Optional filter by items with special features.</param> /// <param name="hasTrailer">Optional filter by items with trailers.</param> /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param> + /// <param name="indexNumber">Optional filter by index number.</param> /// <param name="parentIndexNumber">Optional filter by parent index number.</param> /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param> /// <param name="isHd">Optional filter by items that are HD or not.</param> @@ -165,6 +166,7 @@ public class ItemsController : BaseJellyfinApiController [FromQuery] bool? hasSpecialFeature, [FromQuery] bool? hasTrailer, [FromQuery] Guid? adjacentTo, + [FromQuery] int? indexNumber, [FromQuery] int? parentIndexNumber, [FromQuery] bool? hasParentalRating, [FromQuery] bool? isHd, @@ -366,6 +368,7 @@ public class ItemsController : BaseJellyfinApiController MinCommunityRating = minCommunityRating, MinCriticRating = minCriticRating, ParentId = parentId ?? Guid.Empty, + IndexNumber = indexNumber, ParentIndexNumber = parentIndexNumber, EnableTotalRecordCount = enableTotalRecordCount, ExcludeItemIds = excludeItemIds, @@ -717,6 +720,7 @@ public class ItemsController : BaseJellyfinApiController hasSpecialFeature, hasTrailer, adjacentTo, + null, parentIndexNumber, hasParentalRating, isHd, diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs index fb9f44d46..93c2393f3 100644 --- a/Jellyfin.Api/Controllers/LibraryStructureController.cs +++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs @@ -180,7 +180,21 @@ public class LibraryStructureController : BaseJellyfinApiController // No need to start if scanning the library because it will handle it if (refreshLibrary) { - await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(false); + await _libraryManager.ValidateTopLibraryFolders(CancellationToken.None, true).ConfigureAwait(false); + var newLib = _libraryManager.GetUserRootFolder().Children.FirstOrDefault(f => f.Path.Equals(newPath, StringComparison.OrdinalIgnoreCase)); + if (newLib is CollectionFolder folder) + { + foreach (var child in folder.GetPhysicalFolders()) + { + await child.RefreshMetadata(CancellationToken.None).ConfigureAwait(false); + await child.ValidateChildren(new Progress<double>(), CancellationToken.None).ConfigureAwait(false); + } + } + else + { + // We don't know if this one can be validated individually, trigger a new validation + await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(false); + } } else { @@ -319,7 +333,7 @@ public class LibraryStructureController : BaseJellyfinApiController public ActionResult UpdateLibraryOptions( [FromBody] UpdateLibraryOptionsDto request) { - var item = _libraryManager.GetItemById<CollectionFolder>(request.Id, User.GetUserId()); + var item = _libraryManager.GetItemById<CollectionFolder>(request.Id); if (item is null) { return NotFound(); diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index 4fbaafa2a..d7d0cc454 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -215,6 +215,7 @@ public class TrailersController : BaseJellyfinApiController hasSpecialFeature, hasTrailer, adjacentTo, + null, parentIndexNumber, hasParentalRating, isHd, diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index 68b4b6b8b..426402667 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -231,6 +231,7 @@ public class TvShowsController : BaseJellyfinApiController var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); + var shouldIncludeMissingEpisodes = (user is not null && user.DisplayMissingEpisodes) || User.GetIsApiKey(); if (seasonId.HasValue) // Season id was supplied. Get episodes by season id. { @@ -240,7 +241,7 @@ public class TvShowsController : BaseJellyfinApiController return NotFound("No season exists with Id " + seasonId); } - episodes = seasonItem.GetEpisodes(user, dtoOptions); + episodes = seasonItem.GetEpisodes(user, dtoOptions, shouldIncludeMissingEpisodes); } else if (season.HasValue) // Season number was supplied. Get episodes by season number { @@ -256,7 +257,7 @@ public class TvShowsController : BaseJellyfinApiController episodes = seasonItem is null ? new List<BaseItem>() - : ((Season)seasonItem).GetEpisodes(user, dtoOptions); + : ((Season)seasonItem).GetEpisodes(user, dtoOptions, shouldIncludeMissingEpisodes); } else // No season number or season id was supplied. Returning all episodes. { @@ -265,7 +266,7 @@ public class TvShowsController : BaseJellyfinApiController return NotFound("Series not found"); } - episodes = series.GetEpisodes(user, dtoOptions).ToList(); + episodes = series.GetEpisodes(user, dtoOptions, shouldIncludeMissingEpisodes).ToList(); } // Filter after the fact in case the ui doesn't want them diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 5faa7bc59..212d678a8 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -385,19 +385,6 @@ public class MediaInfoHelper /// <returns>A <see cref="Task"/> containing the <see cref="LiveStreamResponse"/>.</returns> public async Task<LiveStreamResponse> OpenMediaSource(HttpContext httpContext, LiveStreamRequest request) { - // Enforce more restrictive transcoding profile for LiveTV due to compatability reasons - // Cap the MaxStreamingBitrate to 20Mbps, because we are unable to reliably probe source bitrate, - // which will cause the client to request extremely high bitrate that may fail the player/encoder - request.MaxStreamingBitrate = request.MaxStreamingBitrate > 20000000 ? 20000000 : request.MaxStreamingBitrate; - - if (request.DeviceProfile is not null) - { - // Remove all fmp4 transcoding profiles, because it causes playback error and/or A/V sync issues - // Notably: Some channels won't play on FireFox and LG webOs - // Some channels from HDHomerun will experience A/V sync issues - request.DeviceProfile.TranscodingProfiles = request.DeviceProfile.TranscodingProfiles.Where(p => !string.Equals(p.Container, "mp4", StringComparison.OrdinalIgnoreCase)).ToArray(); - } - var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false); var profile = request.DeviceProfile; diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 6cd466da0..af4a9e689 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -142,6 +142,20 @@ public static class StreamingHelpers } else { + // Enforce more restrictive transcoding profile for LiveTV due to compatability reasons + // Cap the MaxStreamingBitrate to 30Mbps, because we are unable to reliably probe source bitrate, + // which will cause the client to request extremely high bitrate that may fail the player/encoder + streamingRequest.VideoBitRate = streamingRequest.VideoBitRate > 30000000 ? 30000000 : streamingRequest.VideoBitRate; + + if (streamingRequest.SegmentContainer is not null) + { + // Remove all fmp4 transcoding profiles, because it causes playback error and/or A/V sync issues + // Notably: Some channels won't play on FireFox and LG webOS + // Some channels from HDHomerun will experience A/V sync issues + streamingRequest.SegmentContainer = "ts"; + streamingRequest.VideoCodec = "h264"; + } + var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(streamingRequest.LiveStreamId, cancellationToken).ConfigureAwait(false); mediaSource = liveStreamInfo.Item1; state.DirectStreamProvider = liveStreamInfo.Item2; |
