aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Api/Controllers')
-rw-r--r--Jellyfin.Api/Controllers/ApiKeyController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ArtistsController.cs18
-rw-r--r--Jellyfin.Api/Controllers/DlnaServerController.cs7
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs173
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs11
-rw-r--r--Jellyfin.Api/Controllers/InstantMixController.cs10
-rw-r--r--Jellyfin.Api/Controllers/ItemLookupController.cs3
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs21
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs18
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs19
-rw-r--r--Jellyfin.Api/Controllers/PersonsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs9
-rw-r--r--Jellyfin.Api/Controllers/SuggestionsController.cs9
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs33
-rw-r--r--Jellyfin.Api/Controllers/UserLibraryController.cs19
-rw-r--r--Jellyfin.Api/Controllers/UserViewsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs7
-rw-r--r--Jellyfin.Api/Controllers/YearsController.cs8
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;
}