diff options
Diffstat (limited to 'Jellyfin.Api/Controllers')
18 files changed, 137 insertions, 246 deletions
diff --git a/Jellyfin.Api/Controllers/ApiKeyController.cs b/Jellyfin.Api/Controllers/ApiKeyController.cs index 8e0332d3e..593846adc 100644 --- a/Jellyfin.Api/Controllers/ApiKeyController.cs +++ b/Jellyfin.Api/Controllers/ApiKeyController.cs @@ -38,11 +38,7 @@ namespace Jellyfin.Api.Controllers { var keys = await _authenticationManager.GetApiKeys(); - return new QueryResult<AuthenticationInfo> - { - Items = keys, - TotalRecordCount = keys.Count - }; + return new QueryResult<AuthenticationInfo>(keys); } /// <summary> diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index 3df975563..b54825775 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -243,11 +243,10 @@ namespace Jellyfin.Api.Controllers return dto; }); - return new QueryResult<BaseItemDto> - { - Items = dtos.ToArray(), - TotalRecordCount = result.TotalRecordCount - }; + return new QueryResult<BaseItemDto>( + query.StartIndex, + result.TotalRecordCount, + dtos.ToArray()); } /// <summary> @@ -447,11 +446,10 @@ namespace Jellyfin.Api.Controllers return dto; }); - return new QueryResult<BaseItemDto> - { - Items = dtos.ToArray(), - TotalRecordCount = result.TotalRecordCount - }; + return new QueryResult<BaseItemDto>( + query.StartIndex, + result.TotalRecordCount, + dtos.ToArray()); } /// <summary> diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 4e8c01577..b1c576c33 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -9,6 +9,7 @@ using Emby.Dlna.Main; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Dlna; +using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -337,11 +338,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var contentType = "image/" + Path.GetExtension(fileName) - .TrimStart('.') - .ToLowerInvariant(); - - return File(icon.Stream, contentType); + return File(icon.Stream, MimeTypes.GetMimeType(fileName)); } private string GetAbsoluteUri() diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 6ef3a2ff9..f2fdeeea5 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -13,6 +13,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; +using Jellyfin.MediaEncoding.Hls.Playlist; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; @@ -28,7 +29,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; namespace Jellyfin.Api.Controllers { @@ -55,6 +55,7 @@ namespace Jellyfin.Api.Controllers private readonly TranscodingJobHelper _transcodingJobHelper; private readonly ILogger<DynamicHlsController> _logger; private readonly EncodingHelper _encodingHelper; + private readonly IDynamicHlsPlaylistGenerator _dynamicHlsPlaylistGenerator; private readonly DynamicHlsHelper _dynamicHlsHelper; private readonly EncodingOptions _encodingOptions; @@ -74,6 +75,7 @@ namespace Jellyfin.Api.Controllers /// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param> /// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</param> /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param> + /// <param name="dynamicHlsPlaylistGenerator">Instance of <see cref="IDynamicHlsPlaylistGenerator"/>.</param> public DynamicHlsController( ILibraryManager libraryManager, IUserManager userManager, @@ -87,7 +89,8 @@ namespace Jellyfin.Api.Controllers TranscodingJobHelper transcodingJobHelper, ILogger<DynamicHlsController> logger, DynamicHlsHelper dynamicHlsHelper, - EncodingHelper encodingHelper) + EncodingHelper encodingHelper, + IDynamicHlsPlaylistGenerator dynamicHlsPlaylistGenerator) { _libraryManager = libraryManager; _userManager = userManager; @@ -102,6 +105,7 @@ namespace Jellyfin.Api.Controllers _logger = logger; _dynamicHlsHelper = dynamicHlsHelper; _encodingHelper = encodingHelper; + _dynamicHlsPlaylistGenerator = dynamicHlsPlaylistGenerator; _encodingOptions = serverConfigurationManager.GetEncodingOptions(); } @@ -385,6 +389,8 @@ namespace Jellyfin.Api.Controllers /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param> /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param> /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param> + /// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param> + /// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param> /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param> /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param> /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param> @@ -441,6 +447,8 @@ namespace Jellyfin.Api.Controllers [FromQuery] long? startTimeTicks, [FromQuery] int? width, [FromQuery] int? height, + [FromQuery] int? maxWidth, + [FromQuery] int? maxHeight, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, [FromQuery] SubtitleDeliveryMethod? subtitleMethod, @@ -493,6 +501,8 @@ namespace Jellyfin.Api.Controllers StartTimeTicks = startTimeTicks, Width = width, Height = height, + MaxWidth = maxWidth, + MaxHeight = maxHeight, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, @@ -717,6 +727,8 @@ namespace Jellyfin.Api.Controllers /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param> /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param> /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param> + /// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param> + /// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param> /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param> /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param> /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param> @@ -771,6 +783,8 @@ namespace Jellyfin.Api.Controllers [FromQuery] long? startTimeTicks, [FromQuery] int? width, [FromQuery] int? height, + [FromQuery] int? maxWidth, + [FromQuery] int? maxHeight, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, [FromQuery] SubtitleDeliveryMethod? subtitleMethod, @@ -823,6 +837,8 @@ namespace Jellyfin.Api.Controllers StartTimeTicks = startTimeTicks, Width = width, Height = height, + MaxWidth = maxWidth, + MaxHeight = maxHeight, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, @@ -844,7 +860,7 @@ namespace Jellyfin.Api.Controllers StreamOptions = streamOptions }; - return await GetVariantPlaylistInternal(streamingRequest, "main", cancellationTokenSource) + return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) .ConfigureAwait(false); } @@ -1009,7 +1025,7 @@ namespace Jellyfin.Api.Controllers StreamOptions = streamOptions }; - return await GetVariantPlaylistInternal(streamingRequest, "main", cancellationTokenSource) + return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource) .ConfigureAwait(false); } @@ -1020,13 +1036,15 @@ namespace Jellyfin.Api.Controllers /// <param name="playlistId">The playlist id.</param> /// <param name="segmentId">The segment id.</param> /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param> + /// <param name="runtimeTicks">The position of the requested segment in ticks.</param> + /// <param name="actualSegmentLengthTicks">The length of the requested segment in ticks.</param> /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param> /// <param name="params">The streaming parameters.</param> /// <param name="tag">The tag.</param> /// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param> /// <param name="playSessionId">The play session id.</param> /// <param name="segmentContainer">The segment container.</param> - /// <param name="segmentLength">The segment lenght.</param> + /// <param name="segmentLength">The desired segment length.</param> /// <param name="minSegments">The minimum number of segments.</param> /// <param name="mediaSourceId">The media version id, if playing an alternate version.</param> /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param> @@ -1048,6 +1066,8 @@ namespace Jellyfin.Api.Controllers /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param> /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param> /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param> + /// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param> + /// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param> /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param> /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param> /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param> @@ -1078,6 +1098,8 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string playlistId, [FromRoute, Required] int segmentId, [FromRoute, Required] string container, + [FromQuery, Required] long runtimeTicks, + [FromQuery, Required] long actualSegmentLengthTicks, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -1106,6 +1128,8 @@ namespace Jellyfin.Api.Controllers [FromQuery] long? startTimeTicks, [FromQuery] int? width, [FromQuery] int? height, + [FromQuery] int? maxWidth, + [FromQuery] int? maxHeight, [FromQuery] int? videoBitRate, [FromQuery] int? subtitleStreamIndex, [FromQuery] SubtitleDeliveryMethod? subtitleMethod, @@ -1129,6 +1153,8 @@ namespace Jellyfin.Api.Controllers var streamingRequest = new VideoRequestDto { Id = itemId, + CurrentRuntimeTicks = runtimeTicks, + ActualSegmentLengthTicks = actualSegmentLengthTicks, Container = container, Static = @static ?? false, Params = @params, @@ -1158,6 +1184,8 @@ namespace Jellyfin.Api.Controllers StartTimeTicks = startTimeTicks, Width = width, Height = height, + MaxWidth = maxWidth, + MaxHeight = maxHeight, VideoBitRate = videoBitRate, SubtitleStreamIndex = subtitleStreamIndex, SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, @@ -1190,6 +1218,8 @@ namespace Jellyfin.Api.Controllers /// <param name="playlistId">The playlist id.</param> /// <param name="segmentId">The segment id.</param> /// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param> + /// <param name="runtimeTicks">The position of the requested segment in ticks.</param> + /// <param name="actualSegmentLengthTicks">The length of the requested segment in ticks.</param> /// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param> /// <param name="params">The streaming parameters.</param> /// <param name="tag">The tag.</param> @@ -1249,6 +1279,8 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string playlistId, [FromRoute, Required] int segmentId, [FromRoute, Required] string container, + [FromQuery, Required] long runtimeTicks, + [FromQuery, Required] long actualSegmentLengthTicks, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -1302,6 +1334,8 @@ namespace Jellyfin.Api.Controllers { Id = itemId, Container = container, + CurrentRuntimeTicks = runtimeTicks, + ActualSegmentLengthTicks = actualSegmentLengthTicks, Static = @static ?? false, Params = @params, Tag = tag, @@ -1355,7 +1389,7 @@ namespace Jellyfin.Api.Controllers .ConfigureAwait(false); } - private async Task<ActionResult> GetVariantPlaylistInternal(StreamingRequestDto streamingRequest, string name, CancellationTokenSource cancellationTokenSource) + private async Task<ActionResult> GetVariantPlaylistInternal(StreamingRequestDto streamingRequest, CancellationTokenSource cancellationTokenSource) { using var state = await StreamingHelpers.GetStreamingState( streamingRequest, @@ -1374,60 +1408,16 @@ namespace Jellyfin.Api.Controllers cancellationTokenSource.Token) .ConfigureAwait(false); - Response.Headers.Add(HeaderNames.Expires, "0"); - - var segmentLengths = GetSegmentLengths(state); - - var segmentContainer = state.Request.SegmentContainer ?? "ts"; - - // http://ffmpeg.org/ffmpeg-all.html#toc-hls-2 - var isHlsInFmp4 = string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase); - var hlsVersion = isHlsInFmp4 ? "7" : "3"; - - var builder = new StringBuilder(128); - - builder.AppendLine("#EXTM3U") - .AppendLine("#EXT-X-PLAYLIST-TYPE:VOD") - .Append("#EXT-X-VERSION:") - .Append(hlsVersion) - .AppendLine() - .Append("#EXT-X-TARGETDURATION:") - .Append(Math.Ceiling(segmentLengths.Length > 0 ? segmentLengths.Max() : state.SegmentLength)) - .AppendLine() - .AppendLine("#EXT-X-MEDIA-SEQUENCE:0"); + var request = new CreateMainPlaylistRequest( + state.MediaPath, + state.SegmentLength * 1000, + state.RunTimeTicks ?? 0, + state.Request.SegmentContainer ?? string.Empty, + "hls1/main/", + Request.QueryString.ToString()); + var playlist = _dynamicHlsPlaylistGenerator.CreateMainPlaylist(request); - var index = 0; - var segmentExtension = EncodingHelper.GetSegmentFileExtension(streamingRequest.SegmentContainer); - var queryString = Request.QueryString; - - if (isHlsInFmp4) - { - builder.Append("#EXT-X-MAP:URI=\"") - .Append("hls1/") - .Append(name) - .Append("/-1") - .Append(segmentExtension) - .Append(queryString) - .Append('"') - .AppendLine(); - } - - foreach (var length in segmentLengths) - { - builder.Append("#EXTINF:") - .Append(length.ToString("0.0000", CultureInfo.InvariantCulture)) - .AppendLine(", nodesc") - .Append("hls1/") - .Append(name) - .Append('/') - .Append(index++) - .Append(segmentExtension) - .Append(queryString) - .AppendLine(); - } - - builder.AppendLine("#EXT-X-ENDLIST"); - return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8")); + return new FileContentResult(Encoding.UTF8.GetBytes(playlist), MimeTypes.GetMimeType("playlist.m3u8")); } private async Task<ActionResult> GetDynamicSegment(StreamingRequestDto streamingRequest, int segmentId) @@ -1528,7 +1518,7 @@ namespace Jellyfin.Api.Controllers DeleteLastFile(playlistPath, segmentExtension, 0); } - streamingRequest.StartTimeTicks = GetStartPositionTicks(state, segmentId); + streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks; state.WaitForPath = segmentPath; job = await _transcodingJobHelper.StartFfMpeg( @@ -1879,7 +1869,7 @@ namespace Jellyfin.Api.Controllers { // Transcoding job is over, so assume all existing files are ready _logger.LogDebug("serving up {0} as transcode is over", segmentPath); - return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob); + return GetSegmentResult(state, segmentPath, transcodingJob); } var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); @@ -1888,7 +1878,7 @@ namespace Jellyfin.Api.Controllers if (segmentIndex < currentTranscodingIndex) { _logger.LogDebug("serving up {0} as transcode index {1} is past requested point {2}", segmentPath, currentTranscodingIndex, segmentIndex); - return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob); + return GetSegmentResult(state, segmentPath, transcodingJob); } } @@ -1903,8 +1893,8 @@ namespace Jellyfin.Api.Controllers { if (transcodingJob.HasExited || System.IO.File.Exists(nextSegmentPath)) { - _logger.LogDebug("serving up {0} as it deemed ready", segmentPath); - return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob); + _logger.LogDebug("Serving up {SegmentPath} as it deemed ready", segmentPath); + return GetSegmentResult(state, segmentPath, transcodingJob); } } else @@ -1935,16 +1925,16 @@ namespace Jellyfin.Api.Controllers _logger.LogWarning("cannot serve {0} as it doesn't exist and no transcode is running", segmentPath); } - return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob); + return GetSegmentResult(state, segmentPath, transcodingJob); } - private ActionResult GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJobDto? transcodingJob) + private ActionResult GetSegmentResult(StreamState state, string segmentPath, TranscodingJobDto? transcodingJob) { - var segmentEndingPositionTicks = GetEndPositionTicks(state, index); + var segmentEndingPositionTicks = state.Request.CurrentRuntimeTicks + state.Request.ActualSegmentLengthTicks; Response.OnCompleted(() => { - _logger.LogDebug("finished serving {0}", segmentPath); + _logger.LogDebug("Finished serving {SegmentPath}", segmentPath); if (transcodingJob != null) { transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks); @@ -1957,29 +1947,6 @@ namespace Jellyfin.Api.Controllers return FileStreamResponseHelpers.GetStaticFileResult(segmentPath, MimeTypes.GetMimeType(segmentPath), false, HttpContext); } - private long GetEndPositionTicks(StreamState state, int requestedIndex) - { - double startSeconds = 0; - var lengths = GetSegmentLengths(state); - - if (requestedIndex >= lengths.Length) - { - var msg = string.Format( - CultureInfo.InvariantCulture, - "Invalid segment index requested: {0} - Segment count: {1}", - requestedIndex, - lengths.Length); - throw new ArgumentException(msg); - } - - for (var i = 0; i <= requestedIndex; i++) - { - startSeconds += lengths[i]; - } - - return TimeSpan.FromSeconds(startSeconds).Ticks; - } - private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension) { var job = _transcodingJobHelper.GetTranscodingJob(playlist, TranscodingJobType); @@ -2058,29 +2025,5 @@ namespace Jellyfin.Api.Controllers _logger.LogError(ex, "Error deleting partial stream file(s) {Path}", path); } } - - private long GetStartPositionTicks(StreamState state, int requestedIndex) - { - double startSeconds = 0; - var lengths = GetSegmentLengths(state); - - if (requestedIndex >= lengths.Length) - { - var msg = string.Format( - CultureInfo.InvariantCulture, - "Invalid segment index requested: {0} - Segment count: {1}", - requestedIndex, - lengths.Length); - throw new ArgumentException(msg); - } - - for (var i = 0; i < requestedIndex; i++) - { - startSeconds += lengths[i]; - } - - var position = TimeSpan.FromSeconds(startSeconds).Ticks; - return position; - } } } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 5997ac549..aafffc2a1 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -2023,8 +2023,8 @@ namespace Jellyfin.Api.Controllers if (!supportsWebP) { var userAgent = Request.Headers[HeaderNames.UserAgent].ToString(); - if (userAgent.IndexOf("crosswalk", StringComparison.OrdinalIgnoreCase) != -1 && - userAgent.IndexOf("android", StringComparison.OrdinalIgnoreCase) != -1) + if (userAgent.Contains("crosswalk", StringComparison.OrdinalIgnoreCase) + && userAgent.Contains("android", StringComparison.OrdinalIgnoreCase)) { supportsWebP = true; } @@ -2050,10 +2050,7 @@ namespace Jellyfin.Api.Controllers private bool SupportsFormat(IReadOnlyCollection<string> requestAcceptTypes, string acceptParam, ImageFormat format, bool acceptAll) { - var normalized = format.ToString().ToLowerInvariant(); - var mimeType = "image/" + normalized; - - if (requestAcceptTypes.Contains(mimeType)) + if (requestAcceptTypes.Contains(format.GetMimeType())) { return true; } @@ -2063,6 +2060,8 @@ namespace Jellyfin.Api.Controllers return true; } + // Review if this should be jpeg, jpg or both for ImageFormat.Jpg + var normalized = format.ToString().ToLowerInvariant(); return string.Equals(acceptParam, normalized, StringComparison.OrdinalIgnoreCase); } diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index a6c2e07c9..e9d48b624 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -341,10 +341,7 @@ namespace Jellyfin.Api.Controllers { var list = items; - var result = new QueryResult<BaseItemDto> - { - TotalRecordCount = list.Count - }; + var totalCount = list.Count; if (limit.HasValue && limit < list.Count) { @@ -353,7 +350,10 @@ namespace Jellyfin.Api.Controllers var returnList = _dtoService.GetBaseItemDtos(list, dtoOptions, user); - result.Items = returnList; + var result = new QueryResult<BaseItemDto>( + 0, + totalCount, + returnList); return result; } diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index 4161e43f6..c49f85616 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -263,7 +263,8 @@ namespace Jellyfin.Api.Controllers ImageRefreshMode = MetadataRefreshMode.FullRefresh, ReplaceAllMetadata = true, ReplaceAllImages = replaceAllImages, - SearchResult = searchResult + SearchResult = searchResult, + RemoveOldMetadata = true }, CancellationToken.None).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 1b938f4d5..dc7af0a20 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -228,9 +228,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true) { - var user = !userId.Equals(Guid.Empty) - ? _userManager.GetUserById(userId) - : null; + var user = userId == Guid.Empty ? null : _userManager.GetUserById(userId); var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(Request) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); @@ -493,10 +491,13 @@ namespace Jellyfin.Api.Controllers else { var itemsArray = folder.GetChildren(user, true); - result = new QueryResult<BaseItem> { Items = itemsArray, TotalRecordCount = itemsArray.Count, StartIndex = 0 }; + result = new QueryResult<BaseItem>(itemsArray); } - return new QueryResult<BaseItemDto> { StartIndex = startIndex.GetValueOrDefault(), TotalRecordCount = result.TotalRecordCount, Items = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user) }; + return new QueryResult<BaseItemDto>( + startIndex, + result.TotalRecordCount, + _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user)); } /// <summary> @@ -838,12 +839,10 @@ namespace Jellyfin.Api.Controllers var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, dtoOptions, user); - return new QueryResult<BaseItemDto> - { - StartIndex = startIndex.GetValueOrDefault(), - TotalRecordCount = itemsResult.TotalRecordCount, - Items = returnItems - }; + return new QueryResult<BaseItemDto>( + startIndex, + itemsResult.TotalRecordCount, + returnItems); } } } diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index f1b9c2f67..c65462ab5 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -506,13 +506,8 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions().AddClientFields(Request); - var result = new QueryResult<BaseItemDto> - { - TotalRecordCount = items.Count, - Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions)).ToArray() - }; - - return result; + var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions); + return new QueryResult<BaseItemDto>(resultArray); } /// <summary> @@ -759,11 +754,10 @@ namespace Jellyfin.Api.Controllers var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user); - return new QueryResult<BaseItemDto> - { - Items = returnList, - TotalRecordCount = itemsResult.Count - }; + return new QueryResult<BaseItemDto>( + query.StartIndex, + itemsResult.Count, + returnList); } /// <summary> diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 9e2ef8c60..484b0a974 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -193,11 +193,10 @@ namespace Jellyfin.Api.Controllers dtoOptions.AddCurrentProgram = addCurrentProgram; var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, dtoOptions, user); - return new QueryResult<BaseItemDto> - { - Items = returnArray, - TotalRecordCount = channelResult.TotalRecordCount - }; + return new QueryResult<BaseItemDto>( + startIndex, + channelResult.TotalRecordCount, + returnArray); } /// <summary> @@ -390,11 +389,7 @@ namespace Jellyfin.Api.Controllers var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user); - return new QueryResult<BaseItemDto> - { - Items = returnArray, - TotalRecordCount = returnArray.Count - }; + return new QueryResult<BaseItemDto>(returnArray); } /// <summary> @@ -687,7 +682,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Programs/Recommended")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<QueryResult<BaseItemDto>> GetRecommendedPrograms( + public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecommendedPrograms( [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] bool? isAiring, @@ -726,7 +721,7 @@ namespace Jellyfin.Api.Controllers var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(Request) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - return _liveTvManager.GetRecommendedPrograms(query, dtoOptions, CancellationToken.None); + return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } /// <summary> diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index cb4894d77..ffc748a6e 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -101,11 +101,7 @@ namespace Jellyfin.Api.Controllers Limit = limit ?? 0 }); - return new QueryResult<BaseItemDto> - { - Items = peopleItems.Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user)).ToArray(), - TotalRecordCount = peopleItems.Count - }; + return new QueryResult<BaseItemDto>(peopleItems.Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user)).ToArray()); } /// <summary> diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index 1667d6ede..c18f1b427 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -208,11 +208,10 @@ namespace Jellyfin.Api.Controllers dtos[index].PlaylistItemId = items[index].Item1.Id; } - var result = new QueryResult<BaseItemDto> - { - Items = dtos, - TotalRecordCount = count - }; + var result = new QueryResult<BaseItemDto>( + startIndex, + count, + dtos); return result; } diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index af77c801f..73be26bb2 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -81,11 +81,10 @@ namespace Jellyfin.Api.Controllers var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user); - return new QueryResult<BaseItemDto> - { - TotalRecordCount = result.TotalRecordCount, - Items = dtoList - }; + return new QueryResult<BaseItemDto>( + startIndex, + result.TotalRecordCount, + dtoList); } } } diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index e20bcd7a7..9425fe519 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -110,11 +110,10 @@ namespace Jellyfin.Api.Controllers var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user); - return new QueryResult<BaseItemDto> - { - TotalRecordCount = result.TotalRecordCount, - Items = returnItems - }; + return new QueryResult<BaseItemDto>( + startIndex, + result.TotalRecordCount, + returnItems); } /// <summary> @@ -169,11 +168,10 @@ namespace Jellyfin.Api.Controllers var returnItems = _dtoService.GetBaseItemDtos(itemsResult, options, user); - return new QueryResult<BaseItemDto> - { - TotalRecordCount = itemsResult.Count, - Items = returnItems - }; + return new QueryResult<BaseItemDto>( + startIndex, + itemsResult.Count, + returnItems); } /// <summary> @@ -296,11 +294,10 @@ namespace Jellyfin.Api.Controllers var dtos = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user); - return new QueryResult<BaseItemDto> - { - TotalRecordCount = episodes.Count, - Items = dtos - }; + return new QueryResult<BaseItemDto>( + startIndex, + episodes.Count, + dtos); } /// <summary> @@ -354,11 +351,7 @@ namespace Jellyfin.Api.Controllers var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user); - return new QueryResult<BaseItemDto> - { - TotalRecordCount = returnItems.Count, - Items = returnItems - }; + return new QueryResult<BaseItemDto>(returnItems); } /// <summary> diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 8b99170d9..008d2f176 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -124,11 +124,7 @@ namespace Jellyfin.Api.Controllers var dtoOptions = new DtoOptions().AddClientFields(Request); var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); - return new QueryResult<BaseItemDto> - { - Items = dtos, - TotalRecordCount = dtos.Length - }; + return new QueryResult<BaseItemDto>(dtos); } /// <summary> @@ -206,21 +202,16 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(itemId); var dtoOptions = new DtoOptions().AddClientFields(Request); - var dtosExtras = item.GetExtras(new[] { ExtraType.Trailer }) - .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) - .ToArray(); if (item is IHasTrailers hasTrailers) { var trailers = hasTrailers.LocalTrailers; - var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item); - var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count]; - dtosExtras.CopyTo(allTrailers, 0); - dtosTrailers.CopyTo(allTrailers, dtosExtras.Length); - return allTrailers; + return Ok(_dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item)); } - return dtosExtras; + return Ok(item.GetExtras() + .Where(e => e.ExtraType == ExtraType.Trailer) + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))); } /// <summary> diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 3d27371f6..04171da8a 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -108,11 +108,7 @@ namespace Jellyfin.Api.Controllers var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) .ToArray(); - return new QueryResult<BaseItemDto> - { - Items = dtos, - TotalRecordCount = dtos.Length - }; + return new QueryResult<BaseItemDto>(dtos); } /// <summary> diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 3c079a71d..89b150598 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -134,12 +134,7 @@ namespace Jellyfin.Api.Controllers items = Array.Empty<BaseItemDto>(); } - var result = new QueryResult<BaseItemDto> - { - Items = items, - TotalRecordCount = items.Length - }; - + var result = new QueryResult<BaseItemDto>(items); return result; } diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index 8be6fd1b5..bac77d43b 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -136,8 +136,6 @@ namespace Jellyfin.Api.Controllers IEnumerable<BaseItem> ibnItems = ibnItemsArray; - var result = new QueryResult<BaseItemDto> { TotalRecordCount = ibnItemsArray.Count }; - if (startIndex.HasValue || limit.HasValue) { if (startIndex.HasValue) @@ -155,8 +153,10 @@ namespace Jellyfin.Api.Controllers var dtos = tuples.Select(i => _dtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user)); - result.Items = dtos.Where(i => i != null).ToArray(); - + var result = new QueryResult<BaseItemDto>( + startIndex, + ibnItemsArray.Count, + dtos.Where(i => i != null).ToArray()); return result; } |
