aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Api')
-rw-r--r--Jellyfin.Api/Controllers/AudioController.cs8
-rw-r--r--Jellyfin.Api/Controllers/DevicesController.cs16
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs34
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs15
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs18
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs10
-rw-r--r--Jellyfin.Api/Controllers/MediaSegmentsController.cs61
-rw-r--r--Jellyfin.Api/Controllers/PluginsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/TrickplayController.cs1
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs13
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs23
-rw-r--r--Jellyfin.Api/Controllers/UserLibraryController.cs2
-rw-r--r--Jellyfin.Api/Controllers/UserViewsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs8
-rw-r--r--Jellyfin.Api/Helpers/DynamicHlsHelper.cs11
-rw-r--r--Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs4
-rw-r--r--Jellyfin.Api/Helpers/RequestHelpers.cs13
-rw-r--r--Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs5
18 files changed, 178 insertions, 72 deletions
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index 8954c8ef5..a47c60473 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/Jellyfin.Api/Controllers/AudioController.cs
@@ -46,7 +46,7 @@ public class AudioController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -76,7 +76,7 @@ public class AudioController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -213,7 +213,7 @@ public class AudioController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -243,7 +243,7 @@ public class AudioController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs
index 6d9ec343e..2a2ab4ad1 100644
--- a/Jellyfin.Api/Controllers/DevicesController.cs
+++ b/Jellyfin.Api/Controllers/DevicesController.cs
@@ -47,10 +47,10 @@ public class DevicesController : BaseJellyfinApiController
/// <returns>An <see cref="OkResult"/> containing the list of devices.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<QueryResult<DeviceInfo>>> GetDevices([FromQuery] Guid? userId)
+ public ActionResult<QueryResult<DeviceInfo>> GetDevices([FromQuery] Guid? userId)
{
userId = RequestHelpers.GetUserId(User, userId);
- return await _deviceManager.GetDevicesForUser(userId).ConfigureAwait(false);
+ return _deviceManager.GetDevicesForUser(userId);
}
/// <summary>
@@ -63,9 +63,9 @@ public class DevicesController : BaseJellyfinApiController
[HttpGet("Info")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<DeviceInfo>> GetDeviceInfo([FromQuery, Required] string id)
+ public ActionResult<DeviceInfo> GetDeviceInfo([FromQuery, Required] string id)
{
- var deviceInfo = await _deviceManager.GetDevice(id).ConfigureAwait(false);
+ var deviceInfo = _deviceManager.GetDevice(id);
if (deviceInfo is null)
{
return NotFound();
@@ -84,9 +84,9 @@ public class DevicesController : BaseJellyfinApiController
[HttpGet("Options")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task<ActionResult<DeviceOptions>> GetDeviceOptions([FromQuery, Required] string id)
+ public ActionResult<DeviceOptions> GetDeviceOptions([FromQuery, Required] string id)
{
- var deviceInfo = await _deviceManager.GetDeviceOptions(id).ConfigureAwait(false);
+ var deviceInfo = _deviceManager.GetDeviceOptions(id);
if (deviceInfo is null)
{
return NotFound();
@@ -124,13 +124,13 @@ public class DevicesController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> DeleteDevice([FromQuery, Required] string id)
{
- var existingDevice = await _deviceManager.GetDevice(id).ConfigureAwait(false);
+ var existingDevice = _deviceManager.GetDevice(id);
if (existingDevice is null)
{
return NotFound();
}
- var sessions = await _deviceManager.GetDevices(new DeviceQuery { DeviceId = id }).ConfigureAwait(false);
+ var sessions = _deviceManager.GetDevices(new DeviceQuery { DeviceId = id });
foreach (var session in sessions.Items)
{
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 130c1192f..662e2acbc 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -45,6 +45,7 @@ public class DynamicHlsController : BaseJellyfinApiController
private const TranscodingJobType TranscodingJobType = MediaBrowser.Controller.MediaEncoding.TranscodingJobType.Hls;
private readonly Version _minFFmpegFlacInMp4 = new Version(6, 0);
+ private readonly Version _minFFmpegX265BframeInFmp4 = new Version(7, 0, 1);
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
@@ -116,7 +117,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -146,7 +147,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -355,7 +356,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -387,7 +388,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -531,7 +532,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -562,7 +563,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -700,7 +701,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -732,7 +733,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -871,7 +872,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -902,7 +903,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -1043,7 +1044,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -1075,7 +1076,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -1227,7 +1228,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -1258,7 +1259,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -1851,13 +1852,12 @@ public class DynamicHlsController : BaseJellyfinApiController
args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, isEventPlaylist, startNumber);
// Currently b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now.
- if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)
+ && _mediaEncoder.EncoderVersion < _minFFmpegX265BframeInFmp4)
{
args += " -bf 0";
}
- // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
-
// video processing filters.
var videoProcessParam = _encodingHelper.GetVideoProcessingFilterParam(state, _encodingOptions, codec);
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 8e8accab3..b71199026 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -109,7 +109,7 @@ public class ImageController : BaseJellyfinApiController
return NotFound();
}
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, requestUserId, true))
+ if (!RequestHelpers.AssertCanUpdateUser(HttpContext.User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
}
@@ -203,13 +203,18 @@ public class ImageController : BaseJellyfinApiController
[FromQuery] Guid? userId)
{
var requestUserId = RequestHelpers.GetUserId(User, userId);
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(HttpContext.User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image.");
}
- var user = _userManager.GetUserById(requestUserId);
- if (user?.ProfileImage is null)
+ if (user.ProfileImage is null)
{
return NoContent();
}
@@ -2089,6 +2094,8 @@ public class ImageController : BaseJellyfinApiController
Response.Headers.Append(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
Response.Headers.Append(HeaderNames.Vary, HeaderNames.Accept);
+ Response.Headers.ContentDisposition = "attachment";
+
if (disableCaching)
{
Response.Headers.Append(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index d33634412..828bd5174 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -972,12 +972,17 @@ public class ItemsController : BaseJellyfinApiController
[FromRoute, Required] Guid itemId)
{
var requestUserId = RequestHelpers.GetUserId(User, userId);
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data.");
}
- var user = _userManager.GetUserById(requestUserId) ?? throw new ResourceNotFoundException();
var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
if (item is null)
{
@@ -1023,12 +1028,17 @@ public class ItemsController : BaseJellyfinApiController
[FromBody, Required] UpdateUserItemDataDto userDataDto)
{
var requestUserId = RequestHelpers.GetUserId(User, userId);
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data.");
}
- var user = _userManager.GetUserById(requestUserId) ?? throw new ResourceNotFoundException();
var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
if (item is null)
{
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index 62cb59335..afc93c3a8 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -857,6 +857,16 @@ public class LibraryController : BaseJellyfinApiController
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.ToArray();
+ result.LyricFetchers = plugins
+ .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LyricFetcher))
+ .Select(i => new LibraryOptionInfoDto
+ {
+ Name = i.Name,
+ DefaultEnabled = true
+ })
+ .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
var typeOptions = new List<LibraryTypeOptionsDto>();
foreach (var type in types)
diff --git a/Jellyfin.Api/Controllers/MediaSegmentsController.cs b/Jellyfin.Api/Controllers/MediaSegmentsController.cs
new file mode 100644
index 000000000..e97704d48
--- /dev/null
+++ b/Jellyfin.Api/Controllers/MediaSegmentsController.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Threading.Tasks;
+using Jellyfin.Api.Extensions;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.MediaSegments;
+using MediaBrowser.Model.Querying;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers;
+
+/// <summary>
+/// Media Segments api.
+/// </summary>
+[Authorize]
+public class MediaSegmentsController : BaseJellyfinApiController
+{
+ private readonly IMediaSegmentManager _mediaSegmentManager;
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaSegmentsController"/> class.
+ /// </summary>
+ /// <param name="mediaSegmentManager">MediaSegments Manager.</param>
+ /// <param name="libraryManager">The Library manager.</param>
+ public MediaSegmentsController(IMediaSegmentManager mediaSegmentManager, ILibraryManager libraryManager)
+ {
+ _mediaSegmentManager = mediaSegmentManager;
+ _libraryManager = libraryManager;
+ }
+
+ /// <summary>
+ /// Gets all media segments based on an itemId.
+ /// </summary>
+ /// <param name="itemId">The ItemId.</param>
+ /// <param name="includeSegmentTypes">Optional filter of requested segment types.</param>
+ /// <returns>A list of media segment objects related to the requested itemId.</returns>
+ [HttpGet("{itemId}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task<ActionResult<QueryResult<MediaSegmentDto>>> GetSegmentsAsync(
+ [FromRoute, Required] Guid itemId,
+ [FromQuery] IEnumerable<MediaSegmentType>? includeSegmentTypes = null)
+ {
+ var item = _libraryManager.GetItemById<BaseItem>(itemId, User.GetUserId());
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ var items = await _mediaSegmentManager.GetSegmentsAsync(item.Id, includeSegmentTypes).ConfigureAwait(false);
+ return Ok(new QueryResult<MediaSegmentDto>(items.ToArray()));
+ }
+}
diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs
index 6abd7a23e..53b7349e7 100644
--- a/Jellyfin.Api/Controllers/PluginsController.cs
+++ b/Jellyfin.Api/Controllers/PluginsController.cs
@@ -233,6 +233,8 @@ public class PluginsController : BaseJellyfinApiController
return NotFound();
}
+ Response.Headers.ContentDisposition = "attachment";
+
imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath);
return PhysicalFile(imagePath, MimeTypes.GetMimeType(imagePath));
}
diff --git a/Jellyfin.Api/Controllers/TrickplayController.cs b/Jellyfin.Api/Controllers/TrickplayController.cs
index 0afe053da..60d49af9e 100644
--- a/Jellyfin.Api/Controllers/TrickplayController.cs
+++ b/Jellyfin.Api/Controllers/TrickplayController.cs
@@ -95,6 +95,7 @@ public class TrickplayController : BaseJellyfinApiController
var path = _trickplayManager.GetTrickplayTilePath(item, width, index);
if (System.IO.File.Exists(path))
{
+ Response.Headers.ContentDisposition = "attachment";
return PhysicalFile(path, MediaTypeNames.Image.Jpeg);
}
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index 426402667..914ccd7f9 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -90,7 +90,12 @@ public class TvShowsController : BaseJellyfinApiController
[FromQuery] bool enableResumable = true,
[FromQuery] bool enableRewatching = false)
{
- userId = RequestHelpers.GetUserId(User, userId);
+ var user = _userManager.GetUserById(RequestHelpers.GetUserId(User, userId));
+ if (user is null)
+ {
+ return NotFound();
+ }
+
var options = new DtoOptions { Fields = fields }
.AddClientFields(User)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -102,7 +107,7 @@ public class TvShowsController : BaseJellyfinApiController
ParentId = parentId,
SeriesId = seriesId,
StartIndex = startIndex,
- UserId = userId.Value,
+ User = user,
EnableTotalRecordCount = enableTotalRecordCount,
DisableFirstEpisode = disableFirstEpisode,
NextUpDateCutoff = nextUpDateCutoff ?? DateTime.MinValue,
@@ -111,10 +116,6 @@ public class TvShowsController : BaseJellyfinApiController
},
options);
- var user = userId.IsNullOrEmpty()
- ? null
- : _userManager.GetUserById(userId.Value);
-
var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user);
return new QueryResult<BaseItemDto>(
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index c3923a2ad..2df79c80c 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -274,16 +274,15 @@ public class UserController : BaseJellyfinApiController
[FromBody, Required] UpdateUserPassword request)
{
var requestUserId = userId ?? User.GetUserId();
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
{
- return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password.");
+ return NotFound();
}
- var user = _userManager.GetUserById(requestUserId);
-
- if (user is null)
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
- return NotFound("User not found");
+ return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password.");
}
if (request.ResetPassword)
@@ -386,7 +385,7 @@ public class UserController : BaseJellyfinApiController
return NotFound();
}
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed.");
}
@@ -396,7 +395,7 @@ public class UserController : BaseJellyfinApiController
await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false);
}
- await _userManager.UpdateConfigurationAsync(user.Id, updateUser.Configuration).ConfigureAwait(false);
+ await _userManager.UpdateConfigurationAsync(requestUserId, updateUser.Configuration).ConfigureAwait(false);
return NoContent();
}
@@ -495,7 +494,13 @@ public class UserController : BaseJellyfinApiController
[FromBody, Required] UserConfiguration userConfig)
{
var requestUserId = userId ?? User.GetUserId();
- if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, requestUserId, true))
+ var user = _userManager.GetUserById(requestUserId);
+ if (user is null)
+ {
+ return NotFound();
+ }
+
+ if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
{
return StatusCode(StatusCodes.Status403Forbidden, "User configuration update not allowed");
}
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index 421f1bfb5..e7bf71727 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -560,7 +560,7 @@ public class UserLibraryController : BaseJellyfinApiController
IsPlayed = isPlayed,
Limit = limit,
ParentId = parentId ?? Guid.Empty,
- UserId = requestUserId,
+ User = user,
},
dtoOptions);
diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs
index 01da50d02..e24f78a88 100644
--- a/Jellyfin.Api/Controllers/UserViewsController.cs
+++ b/Jellyfin.Api/Controllers/UserViewsController.cs
@@ -8,6 +8,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.UserViewDtos;
using Jellyfin.Data.Enums;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -69,8 +70,9 @@ public class UserViewsController : BaseJellyfinApiController
[FromQuery] bool includeHidden = false)
{
userId = RequestHelpers.GetUserId(User, userId);
+ var user = _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException();
- var query = new UserViewQuery { UserId = userId.Value, IncludeHidden = includeHidden };
+ var query = new UserViewQuery { User = user, IncludeHidden = includeHidden };
if (includeExternalContent.HasValue)
{
@@ -87,8 +89,6 @@ public class UserViewsController : BaseJellyfinApiController
var dtoOptions = new DtoOptions().AddClientFields(User);
dtoOptions.Fields = [..dtoOptions.Fields, ItemFields.PrimaryImageAspectRatio, ItemFields.DisplayPreferencesId];
- var user = _userManager.GetUserById(userId.Value);
-
var dtos = Array.ConvertAll(folders, i => _dtoService.GetBaseItemDto(i, dtoOptions, user));
return new QueryResult<BaseItemDto>(dtos);
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index 7f9608378..effe7b021 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -267,7 +267,7 @@ public class VideosController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -299,7 +299,7 @@ public class VideosController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
@@ -508,7 +508,7 @@ public class VideosController : BaseJellyfinApiController
/// <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>
- /// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
+ /// <param name="audioCodec">Optional. Specify an audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension.</param>
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
@@ -540,7 +540,7 @@ public class VideosController : BaseJellyfinApiController
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
/// <param name="liveStreamId">The live stream id.</param>
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
- /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vp8, vp9, vpx (deprecated), wmv.</param>
+ /// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension.</param>
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
/// <param name="transcodeReasons">Optional. The transcoding reason.</param>
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index 6f040cfae..ba92d811c 100644
--- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
+++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
@@ -198,6 +198,17 @@ public class DynamicHlsHelper
AddSubtitles(state, subtitleStreams, builder, _httpContextAccessor.HttpContext.User);
}
+ // Video rotation metadata is only supported in fMP4 remuxing
+ if (state.VideoStream is not null
+ && state.VideoRequest is not null
+ && (state.VideoStream?.Rotation ?? 0) != 0
+ && EncodingHelper.IsCopyCodec(state.OutputVideoCodec)
+ && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer)
+ && !string.Equals(state.Request.SegmentContainer, "mp4", StringComparison.OrdinalIgnoreCase))
+ {
+ playlistUrl += "&AllowVideoStreamCopy=false";
+ }
+
var basicPlaylist = AppendPlaylist(builder, state, playlistUrl, totalBitrate, subtitleGroup);
if (state.VideoStream is not null && state.VideoRequest is not null)
diff --git a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
index d0bfa1fbe..fa0db0541 100644
--- a/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
+++ b/Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
@@ -19,12 +19,12 @@ public static class HlsCodecStringHelpers
/// <summary>
/// Codec name for AC-3.
/// </summary>
- public const string AC3 = "mp4a.a5";
+ public const string AC3 = "ac-3";
/// <summary>
/// Codec name for E-AC-3.
/// </summary>
- public const string EAC3 = "mp4a.a6";
+ public const string EAC3 = "ec-3";
/// <summary>
/// Codec name for FLAC.
diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs
index a3d7f471e..1d9c189a0 100644
--- a/Jellyfin.Api/Helpers/RequestHelpers.cs
+++ b/Jellyfin.Api/Helpers/RequestHelpers.cs
@@ -86,18 +86,17 @@ public static class RequestHelpers
/// <summary>
/// Checks if the user can update an entry.
/// </summary>
- /// <param name="userManager">An instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="claimsPrincipal">The <see cref="ClaimsPrincipal"/> for the current request.</param>
- /// <param name="userId">The user id.</param>
+ /// <param name="user">The user id.</param>
/// <param name="restrictUserPreferences">Whether to restrict the user preferences.</param>
/// <returns>A <see cref="bool"/> whether the user can update the entry.</returns>
- internal static bool AssertCanUpdateUser(IUserManager userManager, ClaimsPrincipal claimsPrincipal, Guid userId, bool restrictUserPreferences)
+ internal static bool AssertCanUpdateUser(ClaimsPrincipal claimsPrincipal, User user, bool restrictUserPreferences)
{
var authenticatedUserId = claimsPrincipal.GetUserId();
var isAdministrator = claimsPrincipal.IsInRole(UserRoles.Administrator);
// If they're going to update the record of another user, they must be an administrator
- if (!userId.Equals(authenticatedUserId) && !isAdministrator)
+ if (!user.Id.Equals(authenticatedUserId) && !isAdministrator)
{
return false;
}
@@ -108,12 +107,6 @@ public static class RequestHelpers
return true;
}
- var user = userManager.GetUserById(userId);
- if (user is null)
- {
- throw new ResourceNotFoundException();
- }
-
return user.EnableUserPreferenceAccess;
}
diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs
index 78efacd94..53b5e3b7c 100644
--- a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs
+++ b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs
@@ -24,6 +24,11 @@ public class LibraryOptionsResultDto
public IReadOnlyList<LibraryOptionInfoDto> SubtitleFetchers { get; set; } = Array.Empty<LibraryOptionInfoDto>();
/// <summary>
+ /// Gets or sets the list of lyric fetchers.
+ /// </summary>
+ public IReadOnlyList<LibraryOptionInfoDto> LyricFetchers { get; set; } = Array.Empty<LibraryOptionInfoDto>();
+
+ /// <summary>
/// Gets or sets the type options.
/// </summary>
public IReadOnlyList<LibraryTypeOptionsDto> TypeOptions { get; set; } = Array.Empty<LibraryTypeOptionsDto>();