aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Api/Controllers')
-rw-r--r--Jellyfin.Api/Controllers/ArtistsController.cs6
-rw-r--r--Jellyfin.Api/Controllers/AudioController.cs2
-rw-r--r--Jellyfin.Api/Controllers/ChannelsController.cs12
-rw-r--r--Jellyfin.Api/Controllers/ConfigurationController.cs10
-rw-r--r--Jellyfin.Api/Controllers/DashboardController.cs3
-rw-r--r--Jellyfin.Api/Controllers/DlnaServerController.cs91
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs58
-rw-r--r--Jellyfin.Api/Controllers/FilterController.cs12
-rw-r--r--Jellyfin.Api/Controllers/GenresController.cs27
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs37
-rw-r--r--Jellyfin.Api/Controllers/InstantMixController.cs42
-rw-r--r--Jellyfin.Api/Controllers/ItemRefreshController.cs4
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs133
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs58
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs52
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs10
-rw-r--r--Jellyfin.Api/Controllers/MoviesController.cs6
-rw-r--r--Jellyfin.Api/Controllers/MusicGenresController.cs6
-rw-r--r--Jellyfin.Api/Controllers/PackageController.cs2
-rw-r--r--Jellyfin.Api/Controllers/PersonsController.cs11
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs4
-rw-r--r--Jellyfin.Api/Controllers/SearchController.cs19
-rw-r--r--Jellyfin.Api/Controllers/SessionController.cs6
-rw-r--r--Jellyfin.Api/Controllers/StudiosController.cs6
-rw-r--r--Jellyfin.Api/Controllers/SuggestionsController.cs4
-rw-r--r--Jellyfin.Api/Controllers/TrailersController.cs24
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs34
-rw-r--r--Jellyfin.Api/Controllers/UniversalAudioController.cs5
-rw-r--r--Jellyfin.Api/Controllers/UserController.cs25
-rw-r--r--Jellyfin.Api/Controllers/UserLibraryController.cs12
-rw-r--r--Jellyfin.Api/Controllers/UserViewsController.cs11
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs20
-rw-r--r--Jellyfin.Api/Controllers/YearsController.cs20
33 files changed, 424 insertions, 348 deletions
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index b54825775..44796bcc4 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
User? user = null;
BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Equals(default))
{
user = _userManager.GetUserById(userId.Value);
}
@@ -329,7 +329,7 @@ namespace Jellyfin.Api.Controllers
User? user = null;
BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Equals(default))
{
user = _userManager.GetUserById(userId.Value);
}
@@ -467,7 +467,7 @@ namespace Jellyfin.Api.Controllers
var item = _libraryManager.GetArtist(name, dtoOptions);
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Value.Equals(default))
{
var user = _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs
index 54ac06276..94f7a7b82 100644
--- a/Jellyfin.Api/Controllers/AudioController.cs
+++ b/Jellyfin.Api/Controllers/AudioController.cs
@@ -207,7 +207,7 @@ namespace Jellyfin.Api.Controllers
/// <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 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>
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index 54bd80095..d5b589a3f 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -125,9 +125,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var query = new InternalItemsQuery(user)
{
@@ -199,9 +199,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var query = new InternalItemsQuery(user)
{
diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs
index 60529e990..464fadc06 100644
--- a/Jellyfin.Api/Controllers/ConfigurationController.cs
+++ b/Jellyfin.Api/Controllers/ConfigurationController.cs
@@ -86,21 +86,23 @@ namespace Jellyfin.Api.Controllers
/// Updates named configuration.
/// </summary>
/// <param name="key">Configuration key.</param>
+ /// <param name="configuration">Configuration.</param>
/// <response code="204">Named configuration updated.</response>
/// <returns>Update status.</returns>
[HttpPost("Configuration/{key}")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task<ActionResult> UpdateNamedConfiguration([FromRoute, Required] string key)
+ public ActionResult UpdateNamedConfiguration([FromRoute, Required] string key, [FromBody, Required] JsonDocument configuration)
{
var configurationType = _configurationManager.GetConfigurationType(key);
- var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType, _serializerOptions).ConfigureAwait(false);
- if (configuration == null)
+ var deserializedConfiguration = configuration.Deserialize(configurationType, _serializerOptions);
+
+ if (deserializedConfiguration == null)
{
throw new ArgumentException("Body doesn't contain a valid configuration");
}
- _configurationManager.SaveConfiguration(key, configuration);
+ _configurationManager.SaveConfiguration(key, deserializedConfiguration);
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/DashboardController.cs b/Jellyfin.Api/Controllers/DashboardController.cs
index 87cb418d9..c8411f44b 100644
--- a/Jellyfin.Api/Controllers/DashboardController.cs
+++ b/Jellyfin.Api/Controllers/DashboardController.cs
@@ -4,10 +4,12 @@ using System.IO;
using System.Linq;
using System.Net.Mime;
using Jellyfin.Api.Attributes;
+using Jellyfin.Api.Constants;
using Jellyfin.Api.Models;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Plugins;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
@@ -46,6 +48,7 @@ namespace Jellyfin.Api.Controllers
[HttpGet("web/ConfigurationPages")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
+ [Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<IEnumerable<ConfigurationPageInfo>> GetConfigurationPages(
[FromQuery] bool? enableInMainMenu)
{
diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs
index b1c576c33..401c0197a 100644
--- a/Jellyfin.Api/Controllers/DlnaServerController.cs
+++ b/Jellyfin.Api/Controllers/DlnaServerController.cs
@@ -20,6 +20,7 @@ namespace Jellyfin.Api.Controllers
/// Dlna Server Controller.
/// </summary>
[Route("Dlna")]
+ [DlnaEnabled]
[Authorize(Policy = Policies.AnonymousLanAccessPolicy)]
public class DlnaServerController : BaseJellyfinApiController
{
@@ -55,15 +56,10 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult GetDescriptionXml([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- var url = GetAbsoluteUri();
- var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
- var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress);
- return Ok(xml);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ var url = GetAbsoluteUri();
+ var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
+ var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress);
+ return Ok(xml);
}
/// <summary>
@@ -83,12 +79,7 @@ namespace Jellyfin.Api.Controllers
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
public ActionResult GetContentDirectory([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return Ok(_contentDirectory.GetServiceXml());
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return Ok(_contentDirectory.GetServiceXml());
}
/// <summary>
@@ -108,12 +99,7 @@ namespace Jellyfin.Api.Controllers
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
public ActionResult GetMediaReceiverRegistrar([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return Ok(_mediaReceiverRegistrar.GetServiceXml());
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return Ok(_mediaReceiverRegistrar.GetServiceXml());
}
/// <summary>
@@ -133,12 +119,7 @@ namespace Jellyfin.Api.Controllers
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")]
public ActionResult GetConnectionManager([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return Ok(_connectionManager.GetServiceXml());
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return Ok(_connectionManager.GetServiceXml());
}
/// <summary>
@@ -155,12 +136,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public async Task<ActionResult<ControlResponse>> ProcessContentDirectoryControlRequest([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false);
}
/// <summary>
@@ -177,12 +153,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public async Task<ActionResult<ControlResponse>> ProcessConnectionManagerControlRequest([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false);
}
/// <summary>
@@ -199,12 +170,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public async Task<ActionResult<ControlResponse>> ProcessMediaReceiverRegistrarControlRequest([FromRoute, Required] string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false);
}
/// <summary>
@@ -224,12 +190,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<EventSubscriptionResponse> ProcessMediaReceiverRegistrarEventRequest(string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return ProcessEventRequest(_mediaReceiverRegistrar);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return ProcessEventRequest(_mediaReceiverRegistrar);
}
/// <summary>
@@ -249,12 +210,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<EventSubscriptionResponse> ProcessContentDirectoryEventRequest(string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return ProcessEventRequest(_contentDirectory);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return ProcessEventRequest(_contentDirectory);
}
/// <summary>
@@ -274,12 +230,7 @@ namespace Jellyfin.Api.Controllers
[ProducesFile(MediaTypeNames.Text.Xml)]
public ActionResult<EventSubscriptionResponse> ProcessConnectionManagerEventRequest(string serverId)
{
- if (DlnaEntryPoint.Enabled)
- {
- return ProcessEventRequest(_connectionManager);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return ProcessEventRequest(_connectionManager);
}
/// <summary>
@@ -299,12 +250,7 @@ namespace Jellyfin.Api.Controllers
[ProducesImageFile]
public ActionResult GetIconId([FromRoute, Required] string serverId, [FromRoute, Required] string fileName)
{
- if (DlnaEntryPoint.Enabled)
- {
- return GetIconInternal(fileName);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return GetIconInternal(fileName);
}
/// <summary>
@@ -322,12 +268,7 @@ namespace Jellyfin.Api.Controllers
[ProducesImageFile]
public ActionResult GetIcon([FromRoute, Required] string fileName)
{
- if (DlnaEntryPoint.Enabled)
- {
- return GetIconInternal(fileName);
- }
-
- return StatusCode(StatusCodes.Status503ServiceUnavailable);
+ return GetIconInternal(fileName);
}
private ActionResult GetIconInternal(string fileName)
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index af6916630..3ed80f662 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -121,7 +121,7 @@ namespace Jellyfin.Api.Controllers
/// <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 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>
@@ -285,7 +285,7 @@ namespace Jellyfin.Api.Controllers
// Due to CTS.Token calling ThrowIfDisposed (https://github.com/dotnet/runtime/issues/29970) we have to "cache" the token
// since it gets disposed when ffmpeg exits
var cancellationToken = cancellationTokenSource.Token;
- using var state = await StreamingHelpers.GetStreamingState(
+ var state = await StreamingHelpers.GetStreamingState(
streamingRequest,
Request,
_authContext,
@@ -1414,7 +1414,8 @@ namespace Jellyfin.Api.Controllers
state.RunTimeTicks ?? 0,
state.Request.SegmentContainer ?? string.Empty,
"hls1/main/",
- Request.QueryString.ToString());
+ Request.QueryString.ToString(),
+ EncodingHelper.IsCopyCodec(state.OutputVideoCodec));
var playlist = _dynamicHlsPlaylistGenerator.CreateMainPlaylist(request);
return new FileContentResult(Encoding.UTF8.GetBytes(playlist), MimeTypes.GetMimeType("playlist.m3u8"));
@@ -1431,7 +1432,7 @@ namespace Jellyfin.Api.Controllers
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
- using var state = await StreamingHelpers.GetStreamingState(
+ var state = await StreamingHelpers.GetStreamingState(
streamingRequest,
Request,
_authContext,
@@ -1599,7 +1600,6 @@ namespace Jellyfin.Api.Controllers
state.BaseRequest.BreakOnNonKeyFrames = false;
}
- var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions);
var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath));
@@ -1608,12 +1608,15 @@ namespace Jellyfin.Api.Controllers
var outputExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer);
var outputTsArg = outputPrefix + "%d" + outputExtension;
- var segmentFormat = outputExtension.TrimStart('.');
- if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
+ var segmentFormat = string.Empty;
+ var segmentContainer = outputExtension.TrimStart('.');
+ var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions, segmentContainer);
+
+ if (string.Equals(segmentContainer, "ts", StringComparison.OrdinalIgnoreCase))
{
segmentFormat = "mpegts";
}
- else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase))
{
var outputFmp4HeaderArg = OperatingSystem.IsWindows() switch
{
@@ -1627,7 +1630,8 @@ namespace Jellyfin.Api.Controllers
}
else
{
- _logger.LogError("Invalid HLS segment container: {SegmentFormat}", segmentFormat);
+ _logger.LogError("Invalid HLS segment container: {SegmentContainer}, default to mpegts", segmentContainer);
+ segmentFormat = "mpegts";
}
var maxMuxingQueueSize = _encodingOptions.MaxMuxingQueueSize > 128
@@ -1647,7 +1651,7 @@ namespace Jellyfin.Api.Controllers
CultureInfo.InvariantCulture,
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size {6} -f hls -max_delay 5000000 -hls_time {7} -hls_segment_type {8} -start_number {9}{10} -hls_segment_filename \"{12}\" -hls_playlist_type {11} -hls_list_size 0 -y \"{13}\"",
inputModifier,
- _encodingHelper.GetInputArgument(state, _encodingOptions),
+ _encodingHelper.GetInputArgument(state, _encodingOptions, segmentContainer),
threads,
mapArgs,
GetVideoArguments(state, startNumber, isEventPlaylist),
@@ -1708,20 +1712,30 @@ namespace Jellyfin.Api.Controllers
return audioTranscodeParams;
}
+ // flac and opus are experimental in mp4 muxer
+ var strictArgs = string.Empty;
+
+ if (string.Equals(state.ActualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(state.ActualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase))
+ {
+ strictArgs = " -strict -2";
+ }
+
if (EncodingHelper.IsCopyCodec(audioCodec))
{
var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions);
var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container);
+ var copyArgs = "-codec:a:0 copy" + bitStreamArgs + strictArgs;
if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
{
- return "-codec:a:0 copy -strict -2 -copypriorss:a:0 0" + bitStreamArgs;
+ return copyArgs + " -copypriorss:a:0 0";
}
- return "-codec:a:0 copy -strict -2" + bitStreamArgs;
+ return copyArgs;
}
- var args = "-codec:a:0 " + audioCodec;
+ var args = "-codec:a:0 " + audioCodec + strictArgs;
var channels = state.OutputAudioChannels;
@@ -1770,13 +1784,25 @@ namespace Jellyfin.Api.Controllers
var args = "-codec:v:0 " + codec;
- // Prefer hvc1 to hev1.
if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)
|| string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
{
- args += " -tag:v:0 hvc1";
+ if (EncodingHelper.IsCopyCodec(codec)
+ && (string.Equals(state.VideoStream.VideoRangeType, "DOVI", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(state.VideoStream.CodecTag, "dovi", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(state.VideoStream.CodecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(state.VideoStream.CodecTag, "dvhe", StringComparison.OrdinalIgnoreCase)))
+ {
+ // Prefer dvh1 to dvhe
+ args += " -tag:v:0 dvh1 -strict -2";
+ }
+ else
+ {
+ // Prefer hvc1 to hev1
+ args += " -tag:v:0 hvc1";
+ }
}
// if (state.EnableMpegtsM2TsMode)
@@ -1806,7 +1832,7 @@ namespace Jellyfin.Api.Controllers
// Set the key frame params for video encoding to match the hls segment time.
args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, isEventPlaylist, startNumber);
- // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now.
+ // Currently b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now.
if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase))
{
args += " -bf 0";
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs
index a4f12666d..11808b1b8 100644
--- a/Jellyfin.Api/Controllers/FilterController.cs
+++ b/Jellyfin.Api/Controllers/FilterController.cs
@@ -52,9 +52,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
BaseItem? item = null;
if (includeItemTypes.Length != 1
@@ -144,9 +144,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isSeries,
[FromQuery] bool? recursive)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
BaseItem? parentItem = null;
if (includeItemTypes.Length == 1
diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs
index 37e6ae184..e28a50750 100644
--- a/Jellyfin.Api/Controllers/GenresController.cs
+++ b/Jellyfin.Api/Controllers/GenresController.cs
@@ -95,7 +95,9 @@ namespace Jellyfin.Api.Controllers
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes);
- User? user = userId.HasValue && userId != Guid.Empty ? _userManager.GetUserById(userId.Value) : null;
+ User? user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var parentItem = _libraryManager.GetParentItem(parentId, userId);
@@ -157,29 +159,26 @@ namespace Jellyfin.Api.Controllers
var dtoOptions = new DtoOptions()
.AddClientFields(Request);
- Genre item = new Genre();
- if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1)
+ Genre? item;
+ if (genreName.Contains(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase))
{
- var result = GetItemFromSlugName<Genre>(_libraryManager, genreName, dtoOptions, BaseItemKind.Genre);
-
- if (result != null)
- {
- item = result;
- }
+ item = GetItemFromSlugName<Genre>(_libraryManager, genreName, dtoOptions, BaseItemKind.Genre);
}
else
{
item = _libraryManager.GetGenre(genreName);
}
- if (userId.HasValue && !userId.Equals(Guid.Empty))
- {
- var user = _userManager.GetUserById(userId.Value);
+ item ??= new Genre();
- return _dtoService.GetBaseItemDto(item, dtoOptions, user);
+ if (userId is null || userId.Value.Equals(default))
+ {
+ return _dtoService.GetBaseItemDto(item, dtoOptions);
}
- return _dtoService.GetBaseItemDto(item, dtoOptions);
+ var user = _userManager.GetUserById(userId.Value);
+
+ return _dtoService.GetBaseItemDto(item, dtoOptions, user);
}
private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions, BaseItemKind baseItemKind)
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index 05d80ba35..6c7842c7b 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -1724,6 +1724,11 @@ namespace Jellyfin.Api.Controllers
[FromQuery, Range(0, 100)] int quality = 90)
{
var brandingOptions = _serverConfigurationManager.GetConfiguration<BrandingOptions>("branding");
+ if (!brandingOptions.SplashscreenEnabled)
+ {
+ return NotFound();
+ }
+
string splashscreenPath;
if (!string.IsNullOrWhiteSpace(brandingOptions.SplashscreenLocation)
@@ -1776,6 +1781,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Uploads a custom splashscreen.
+ /// The body is expected to the image contents base64 encoded.
/// </summary>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
/// <response code="204">Successfully uploaded new splashscreen.</response>
@@ -1799,7 +1805,13 @@ namespace Jellyfin.Api.Controllers
return BadRequest("Error reading mimetype from uploaded image");
}
- var filePath = Path.Combine(_appPaths.DataPath, "splashscreen-upload" + MimeTypes.ToExtension(mimeType.Value));
+ var extension = MimeTypes.ToExtension(mimeType.Value);
+ if (string.IsNullOrEmpty(extension))
+ {
+ return BadRequest("Error converting mimetype to an image extension");
+ }
+
+ var filePath = Path.Combine(_appPaths.DataPath, "splashscreen-upload" + extension);
var brandingOptions = _serverConfigurationManager.GetConfiguration<BrandingOptions>("branding");
brandingOptions.SplashscreenLocation = filePath;
_serverConfigurationManager.SaveConfiguration("branding", brandingOptions);
@@ -1812,6 +1824,29 @@ namespace Jellyfin.Api.Controllers
return NoContent();
}
+ /// <summary>
+ /// Delete a custom splashscreen.
+ /// </summary>
+ /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
+ /// <response code="204">Successfully deleted the custom splashscreen.</response>
+ /// <response code="403">User does not have permission to delete splashscreen..</response>
+ [HttpDelete("Branding/Splashscreen")]
+ [Authorize(Policy = Policies.RequiresElevation)]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public ActionResult DeleteCustomSplashscreen()
+ {
+ var brandingOptions = _serverConfigurationManager.GetConfiguration<BrandingOptions>("branding");
+ if (!string.IsNullOrEmpty(brandingOptions.SplashscreenLocation)
+ && System.IO.File.Exists(brandingOptions.SplashscreenLocation))
+ {
+ System.IO.File.Delete(brandingOptions.SplashscreenLocation);
+ brandingOptions.SplashscreenLocation = null;
+ _serverConfigurationManager.SaveConfiguration("branding", brandingOptions);
+ }
+
+ return NoContent();
+ }
+
private static async Task<MemoryStream> GetMemoryStream(Stream inputStream)
{
using var reader = new StreamReader(inputStream);
diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs
index e9d48b624..9abea5938 100644
--- a/Jellyfin.Api/Controllers/InstantMixController.cs
+++ b/Jellyfin.Api/Controllers/InstantMixController.cs
@@ -75,9 +75,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var item = _libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -111,9 +111,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var album = _libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -147,9 +147,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var playlist = (Playlist)_libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -182,9 +182,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -218,9 +218,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var item = _libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -254,9 +254,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var item = _libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -327,9 +327,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var item = _libraryManager.GetItemById(id);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs
index 49865eb5e..9340737b5 100644
--- a/Jellyfin.Api/Controllers/ItemRefreshController.cs
+++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs
@@ -15,7 +15,7 @@ namespace Jellyfin.Api.Controllers
/// Item Refresh Controller.
/// </summary>
[Route("Items")]
- [Authorize(Policy = Policies.DefaultAuthorization)]
+ [Authorize(Policy = Policies.RequiresElevation)]
public class ItemRefreshController : BaseJellyfinApiController
{
private readonly ILibraryManager _libraryManager;
@@ -53,7 +53,7 @@ namespace Jellyfin.Api.Controllers
[Description("Refreshes metadata for an item.")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult Post(
+ public ActionResult RefreshItem(
[FromRoute, Required] Guid itemId,
[FromQuery] MetadataRefreshMode metadataRefreshMode = MetadataRefreshMode.None,
[FromQuery] MetadataRefreshMode imageRefreshMode = MetadataRefreshMode.None,
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index dc7af0a20..4d09070db 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
@@ -9,6 +10,7 @@ using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -32,6 +34,7 @@ namespace Jellyfin.Api.Controllers
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
private readonly IDtoService _dtoService;
+ private readonly IAuthorizationContext _authContext;
private readonly ILogger<ItemsController> _logger;
private readonly ISessionManager _sessionManager;
@@ -42,6 +45,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
+ /// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
public ItemsController(
@@ -49,6 +53,7 @@ namespace Jellyfin.Api.Controllers
ILibraryManager libraryManager,
ILocalizationManager localization,
IDtoService dtoService,
+ IAuthorizationContext authContext,
ILogger<ItemsController> logger,
ISessionManager sessionManager)
{
@@ -56,6 +61,7 @@ namespace Jellyfin.Api.Controllers
_libraryManager = libraryManager;
_localization = localization;
_dtoService = dtoService;
+ _authContext = authContext;
_logger = logger;
_sessionManager = sessionManager;
}
@@ -63,7 +69,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Gets items based on a query.
/// </summary>
- /// <param name="userId">The user id supplied as query parameter.</param>
+ /// <param name="userId">The user id supplied as query parameter; this is required when not using an API key.</param>
/// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
/// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
/// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
@@ -89,6 +95,11 @@ namespace Jellyfin.Api.Controllers
/// <param name="hasImdbId">Optional filter by items that have an imdb id or not.</param>
/// <param name="hasTmdbId">Optional filter by items that have a tmdb id or not.</param>
/// <param name="hasTvdbId">Optional filter by items that have a tvdb id or not.</param>
+ /// <param name="isMovie">Optional filter for live tv movies.</param>
+ /// <param name="isSeries">Optional filter for live tv series.</param>
+ /// <param name="isNews">Optional filter for live tv news.</param>
+ /// <param name="isKids">Optional filter for live tv kids.</param>
+ /// <param name="isSports">Optional filter for live tv sports.</param>
/// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
@@ -146,15 +157,15 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
[HttpGet("Items")]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<QueryResult<BaseItemDto>> GetItems(
- [FromQuery] Guid userId,
+ public async Task<ActionResult<QueryResult<BaseItemDto>>> GetItems(
+ [FromQuery] Guid? userId,
[FromQuery] string? maxOfficialRating,
[FromQuery] bool? hasThemeSong,
[FromQuery] bool? hasThemeVideo,
[FromQuery] bool? hasSubtitles,
[FromQuery] bool? hasSpecialFeature,
[FromQuery] bool? hasTrailer,
- [FromQuery] string? adjacentTo,
+ [FromQuery] Guid? adjacentTo,
[FromQuery] int? parentIndexNumber,
[FromQuery] bool? hasParentalRating,
[FromQuery] bool? isHd,
@@ -173,6 +184,11 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? hasImdbId,
[FromQuery] bool? hasTmdbId,
[FromQuery] bool? hasTvdbId,
+ [FromQuery] bool? isMovie,
+ [FromQuery] bool? isSeries,
+ [FromQuery] bool? isNews,
+ [FromQuery] bool? isKids,
+ [FromQuery] bool? isSports,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
@@ -228,7 +244,19 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool enableTotalRecordCount = true,
[FromQuery] bool? enableImages = true)
{
- var user = userId == Guid.Empty ? null : _userManager.GetUserById(userId);
+ var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false);
+
+ // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
+ var user = !auth.IsApiKey && userId.HasValue && !userId.Value.Equals(default)
+ ? _userManager.GetUserById(userId.Value)
+ : null;
+
+ // beyond this point, we're either using an api key or we have a valid user
+ if (!auth.IsApiKey && user is null)
+ {
+ return BadRequest("userId is required");
+ }
+
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
@@ -260,30 +288,39 @@ namespace Jellyfin.Api.Controllers
includeItemTypes = new[] { BaseItemKind.Playlist };
}
- var enabledChannels = user!.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels);
+ var enabledChannels = auth.IsApiKey
+ ? Array.Empty<Guid>()
+ : user!.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels);
- bool isInEnabledFolder = Array.IndexOf(user.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders), item.Id) != -1
+ // api keys are always enabled for all folders
+ bool isInEnabledFolder = auth.IsApiKey
+ || Array.IndexOf(user!.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders), item.Id) != -1
// Assume all folders inside an EnabledChannel are enabled
|| Array.IndexOf(enabledChannels, item.Id) != -1
// Assume all items inside an EnabledChannel are enabled
|| Array.IndexOf(enabledChannels, item.ChannelId) != -1;
- var collectionFolders = _libraryManager.GetCollectionFolders(item);
- foreach (var collectionFolder in collectionFolders)
+ if (!isInEnabledFolder)
{
- if (user.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id))
+ var collectionFolders = _libraryManager.GetCollectionFolders(item);
+ foreach (var collectionFolder in collectionFolders)
{
- isInEnabledFolder = true;
+ // api keys never enter this block, so user is never null
+ if (user!.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id))
+ {
+ isInEnabledFolder = true;
+ }
}
}
+ // api keys are always enabled for all folders, so user is never null
if (item is not UserRootFolder
&& !isInEnabledFolder
- && !user.HasPermission(PermissionKind.EnableAllFolders)
+ && !user!.HasPermission(PermissionKind.EnableAllFolders)
&& !user.HasPermission(PermissionKind.EnableAllChannels)
&& !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase))
{
- _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name);
+ _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user.Username, item.Name);
return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
}
@@ -316,6 +353,11 @@ namespace Jellyfin.Api.Controllers
Is3D = is3D,
HasTvdbId = hasTvdbId,
HasTmdbId = hasTmdbId,
+ IsMovie = isMovie,
+ IsSeries = isSeries,
+ IsNews = isNews,
+ IsKids = isKids,
+ IsSports = isSports,
HasOverview = hasOverview,
HasOfficialRating = hasOfficialRating,
HasParentalRating = hasParentalRating,
@@ -515,8 +557,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
/// <param name="isHd">Optional filter by items that are HD or not.</param>
/// <param name="is4K">Optional filter by items that are 4K or not.</param>
- /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.</param>
- /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimeted.</param>
+ /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimited.</param>
+ /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimited.</param>
/// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
/// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
/// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
@@ -529,42 +571,47 @@ namespace Jellyfin.Api.Controllers
/// <param name="hasImdbId">Optional filter by items that have an imdb id or not.</param>
/// <param name="hasTmdbId">Optional filter by items that have a tmdb id or not.</param>
/// <param name="hasTvdbId">Optional filter by items that have a tvdb id or not.</param>
- /// <param name="excludeItemIds">Optional. If specified, results will be filtered by exxcluding item ids. This allows multiple, comma delimeted.</param>
+ /// <param name="isMovie">Optional filter for live tv movies.</param>
+ /// <param name="isSeries">Optional filter for live tv series.</param>
+ /// <param name="isNews">Optional filter for live tv news.</param>
+ /// <param name="isKids">Optional filter for live tv kids.</param>
+ /// <param name="isSports">Optional filter for live tv sports.</param>
+ /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="recursive">When searching within folders, this determines whether or not the search will be recursive. true/false.</param>
/// <param name="searchTerm">Optional. Filter based on a search term.</param>
/// <param name="sortOrder">Sort Order - Ascending,Descending.</param>
/// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</param>
- /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
- /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.</param>
- /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimeted.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
+ /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.</param>
+ /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
/// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
/// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
/// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types. This allows multiple, comma delimited.</param>
- /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime.</param>
+ /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime.</param>
/// <param name="isPlayed">Optional filter by items that are played, or not.</param>
- /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.</param>
- /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.</param>
- /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimeted.</param>
- /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.</param>
+ /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.</param>
+ /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimited.</param>
+ /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimited.</param>
+ /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimited.</param>
/// <param name="enableUserData">Optional, include user data.</param>
/// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="person">Optional. If specified, results will be filtered to include only those containing the specified person.</param>
/// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the specified person id.</param>
/// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited.</param>
- /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.</param>
- /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimeted.</param>
- /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimeted.</param>
+ /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimited.</param>
+ /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimited.</param>
+ /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimited.</param>
/// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the specified artist id.</param>
/// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing the specified album artist id.</param>
/// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those containing the specified contributing artist id.</param>
- /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.</param>
- /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimeted.</param>
+ /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimited.</param>
+ /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimited.</param>
/// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited.</param>
- /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimeted.</param>
+ /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimited.</param>
/// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
/// <param name="isLocked">Optional filter by items that are locked.</param>
/// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
@@ -575,18 +622,18 @@ namespace Jellyfin.Api.Controllers
/// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
/// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
/// <param name="is3D">Optional filter by items that are 3D, or not.</param>
- /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimeted.</param>
+ /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
/// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a given input string.</param>
/// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</param>
/// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</param>
- /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimeted.</param>
- /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimeted.</param>
+ /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimited.</param>
+ /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimited.</param>
/// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
/// <param name="enableImages">Optional, include image information in output.</param>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
[HttpGet("Users/{userId}/Items")]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<QueryResult<BaseItemDto>> GetItemsByUserId(
+ public Task<ActionResult<QueryResult<BaseItemDto>>> GetItemsByUserId(
[FromRoute] Guid userId,
[FromQuery] string? maxOfficialRating,
[FromQuery] bool? hasThemeSong,
@@ -594,7 +641,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? hasSubtitles,
[FromQuery] bool? hasSpecialFeature,
[FromQuery] bool? hasTrailer,
- [FromQuery] string? adjacentTo,
+ [FromQuery] Guid? adjacentTo,
[FromQuery] int? parentIndexNumber,
[FromQuery] bool? hasParentalRating,
[FromQuery] bool? isHd,
@@ -613,6 +660,11 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? hasImdbId,
[FromQuery] bool? hasTmdbId,
[FromQuery] bool? hasTvdbId,
+ [FromQuery] bool? isMovie,
+ [FromQuery] bool? isSeries,
+ [FromQuery] bool? isNews,
+ [FromQuery] bool? isKids,
+ [FromQuery] bool? isSports,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
@@ -695,6 +747,11 @@ namespace Jellyfin.Api.Controllers
hasImdbId,
hasTmdbId,
hasTvdbId,
+ isMovie,
+ isSeries,
+ isNews,
+ isKids,
+ isSports,
excludeItemIds,
startIndex,
limit,
@@ -799,7 +856,7 @@ namespace Jellyfin.Api.Controllers
var ancestorIds = Array.Empty<Guid>();
var excludeFolderIds = user.GetPreferenceValues<Guid>(PreferenceKind.LatestItemExcludes);
- if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0)
+ if (parentIdGuid.Equals(default) && excludeFolderIds.Length > 0)
{
ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.Where(i => i is Folder)
@@ -812,7 +869,7 @@ namespace Jellyfin.Api.Controllers
if (excludeActiveSessions)
{
excludeItemIds = _sessionManager.Sessions
- .Where(s => s.UserId == userId && s.NowPlayingItem != null)
+ .Where(s => s.UserId.Equals(userId) && s.NowPlayingItem != null)
.Select(s => s.NowPlayingItem.Id)
.ToArray();
}
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index c65462ab5..4cc17dd0f 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -149,14 +149,14 @@ namespace Jellyfin.Api.Controllers
[FromQuery] Guid? userId,
[FromQuery] bool inheritFromParent = false)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
-
- var item = itemId.Equals(Guid.Empty)
- ? (!userId.Equals(Guid.Empty)
- ? _libraryManager.GetUserRootFolder()
- : _libraryManager.RootFolder)
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
+
+ var item = itemId.Equals(default)
+ ? (userId is null || userId.Value.Equals(default)
+ ? _libraryManager.RootFolder
+ : _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
if (item == null)
@@ -215,14 +215,14 @@ namespace Jellyfin.Api.Controllers
[FromQuery] Guid? userId,
[FromQuery] bool inheritFromParent = false)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
-
- var item = itemId.Equals(Guid.Empty)
- ? (!userId.Equals(Guid.Empty)
- ? _libraryManager.GetUserRootFolder()
- : _libraryManager.RootFolder)
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
+
+ var item = itemId.Equals(default)
+ ? (userId is null || userId.Value.Equals(default)
+ ? _libraryManager.RootFolder
+ : _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
if (item == null)
@@ -407,9 +407,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] Guid? userId,
[FromQuery] bool? isFavorite)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var counts = new ItemCounts
{
@@ -449,9 +449,9 @@ namespace Jellyfin.Api.Controllers
var baseItemDtos = new List<BaseItemDto>();
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions().AddClientFields(Request);
BaseItem? parent = item.GetParent();
@@ -689,10 +689,10 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
{
- var item = itemId.Equals(Guid.Empty)
- ? (!userId.Equals(Guid.Empty)
- ? _libraryManager.GetUserRootFolder()
- : _libraryManager.RootFolder)
+ var item = itemId.Equals(default)
+ ? (userId is null || userId.Value.Equals(default)
+ ? _libraryManager.RootFolder
+ : _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
if (item is Episode || (item is IItemByName && item is not MusicArtist))
@@ -700,9 +700,9 @@ namespace Jellyfin.Api.Controllers
return new QueryResult<BaseItemDto>();
}
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request);
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 484b0a974..05340099b 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -180,9 +180,9 @@ namespace Jellyfin.Api.Controllers
dtoOptions,
CancellationToken.None);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var fieldsList = dtoOptions.Fields.ToList();
fieldsList.Remove(ItemFields.CanDelete);
@@ -211,10 +211,10 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<BaseItemDto> GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
- var item = channelId.Equals(Guid.Empty)
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
+ var item = channelId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(channelId);
@@ -382,9 +382,9 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<QueryResult<BaseItemDto>> GetRecordingFolders([FromQuery] Guid? userId)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var folders = _liveTvManager.GetRecordingFolders(user);
var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user);
@@ -404,10 +404,10 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<BaseItemDto> GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
- var item = recordingId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId);
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
+ var item = recordingId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId);
var dtoOptions = new DtoOptions()
.AddClientFields(Request);
@@ -561,9 +561,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool enableTotalRecordCount = true)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var query = new InternalItemsQuery(user)
{
@@ -588,7 +588,7 @@ namespace Jellyfin.Api.Controllers
GenreIds = genreIds
};
- if (librarySeriesId != null && !librarySeriesId.Equals(Guid.Empty))
+ if (librarySeriesId.HasValue && !librarySeriesId.Equals(default))
{
query.IsSeries = true;
@@ -617,7 +617,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetPrograms([FromBody] GetProgramsDto body)
{
- var user = body.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(body.UserId);
+ var user = body.UserId.Equals(default) ? null : _userManager.GetUserById(body.UserId);
var query = new InternalItemsQuery(user)
{
@@ -642,7 +642,7 @@ namespace Jellyfin.Api.Controllers
GenreIds = body.GenreIds
};
- if (!body.LibrarySeriesId.Equals(Guid.Empty))
+ if (!body.LibrarySeriesId.Equals(default))
{
query.IsSeries = true;
@@ -700,9 +700,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableUserData,
[FromQuery] bool enableTotalRecordCount = true)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var query = new InternalItemsQuery(user)
{
@@ -738,9 +738,9 @@ namespace Jellyfin.Api.Controllers
[FromRoute, Required] string programId,
[FromQuery] Guid? userId)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
return await _liveTvManager.GetProgram(programId, CancellationToken.None, user).ConfigureAwait(false);
}
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index b422eb78c..75df18204 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false);
var profile = playbackInfoDto?.DeviceProfile;
- _logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile);
+ _logger.LogDebug("GetPostedPlaybackInfo profile: {@Profile}", profile);
if (profile == null)
{
@@ -225,14 +225,6 @@ namespace Jellyfin.Api.Controllers
}
}
- if (info.MediaSources != null)
- {
- foreach (var mediaSource in info.MediaSources)
- {
- _mediaInfoHelper.NormalizeMediaSourceContainer(mediaSource, profile!, DlnaProfileType.Video);
- }
- }
-
return info;
}
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index db72ff2f8..420dd9923 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -68,9 +68,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int categoryLimit = 5,
[FromQuery] int itemLimit = 8)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request);
diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs
index c4c03aa4f..0499b2985 100644
--- a/Jellyfin.Api/Controllers/MusicGenresController.cs
+++ b/Jellyfin.Api/Controllers/MusicGenresController.cs
@@ -95,7 +95,9 @@ namespace Jellyfin.Api.Controllers
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes);
- User? user = userId.HasValue && userId != Guid.Empty ? _userManager.GetUserById(userId.Value) : null;
+ User? user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var parentItem = _libraryManager.GetParentItem(parentId, userId);
@@ -156,7 +158,7 @@ namespace Jellyfin.Api.Controllers
item = _libraryManager.GetMusicGenre(genreName);
}
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Value.Equals(default))
{
var user = _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs
index 5dd49ef2f..9690aead0 100644
--- a/Jellyfin.Api/Controllers/PackageController.cs
+++ b/Jellyfin.Api/Controllers/PackageController.cs
@@ -155,7 +155,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="204">Package repositories saved.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Repositories")]
- [Authorize(Policy = Policies.DefaultAuthorization)]
+ [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SetRepositories([FromBody, Required] List<RepositoryInfo> repositoryInfos)
{
diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs
index ffc748a6e..be4b9eded 100644
--- a/Jellyfin.Api/Controllers/PersonsController.cs
+++ b/Jellyfin.Api/Controllers/PersonsController.cs
@@ -82,12 +82,9 @@ namespace Jellyfin.Api.Controllers
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
- User? user = null;
-
- if (userId.HasValue && !userId.Equals(Guid.Empty))
- {
- user = _userManager.GetUserById(userId.Value);
- }
+ User? user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var isFavoriteInFilters = filters.Any(f => f == ItemFilter.IsFavorite);
var peopleItems = _libraryManager.GetPeopleItems(new InternalPeopleQuery(
@@ -127,7 +124,7 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Value.Equals(default))
{
var user = _userManager.GetUserById(userId.Value);
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index c18f1b427..ad85f2fb2 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -181,7 +181,9 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- var user = !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId) : null;
+ var user = userId.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId);
var items = playlist.GetManageableItems().ToArray();
diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs
index 6fcd2ae40..aeed0c0d6 100644
--- a/Jellyfin.Api/Controllers/SearchController.cs
+++ b/Jellyfin.Api/Controllers/SearchController.cs
@@ -6,6 +6,7 @@ using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
+using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -59,9 +60,9 @@ namespace Jellyfin.Api.Controllers
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="userId">Optional. Supply a user id to search within a user's library or omit to search all.</param>
/// <param name="searchTerm">The search term to filter on.</param>
- /// <param name="includeItemTypes">If specified, only results with the specified item types are returned. This allows multiple, comma delimeted.</param>
- /// <param name="excludeItemTypes">If specified, results with these item types are filtered out. This allows multiple, comma delimeted.</param>
- /// <param name="mediaTypes">If specified, only results with the specified media types are returned. This allows multiple, comma delimeted.</param>
+ /// <param name="includeItemTypes">If specified, only results with the specified item types are returned. This allows multiple, comma delimited.</param>
+ /// <param name="excludeItemTypes">If specified, results with these item types are filtered out. This allows multiple, comma delimited.</param>
+ /// <param name="mediaTypes">If specified, only results with the specified media types are returned. This allows multiple, comma delimited.</param>
/// <param name="parentId">If specified, only children of the parent are returned.</param>
/// <param name="isMovie">Optional filter for movies.</param>
/// <param name="isSeries">Optional filter for series.</param>
@@ -78,7 +79,7 @@ namespace Jellyfin.Api.Controllers
[HttpGet]
[Description("Gets search hints based on a search term")]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<SearchHintResult> Get(
+ public ActionResult<SearchHintResult> GetSearchHints(
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] Guid? userId,
@@ -139,7 +140,7 @@ namespace Jellyfin.Api.Controllers
IndexNumber = item.IndexNumber,
ParentIndexNumber = item.ParentIndexNumber,
Id = item.Id,
- Type = item.GetClientTypeName(),
+ Type = item.GetBaseItemKind(),
MediaType = item.MediaType,
MatchedTerm = hintInfo.MatchedTerm,
RunTimeTicks = item.RunTimeTicks,
@@ -148,8 +149,10 @@ namespace Jellyfin.Api.Controllers
EndDate = item.EndDate
};
- // legacy
+#pragma warning disable CS0618
+ // Kept for compatibility with older clients
result.ItemId = result.Id;
+#pragma warning restore CS0618
if (item.IsFolder)
{
@@ -187,7 +190,7 @@ namespace Jellyfin.Api.Controllers
result.AlbumArtist = album.AlbumArtist;
break;
case Audio song:
- result.AlbumArtist = song.AlbumArtists?[0];
+ result.AlbumArtist = song.AlbumArtists?.FirstOrDefault();
result.Artists = song.Artists;
MusicAlbum musicAlbum = song.AlbumEntity;
@@ -205,7 +208,7 @@ namespace Jellyfin.Api.Controllers
break;
}
- if (!item.ChannelId.Equals(Guid.Empty))
+ if (!item.ChannelId.Equals(default))
{
var channel = _libraryManager.GetItemById(item.ChannelId);
result.ChannelName = channel?.Name;
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index a6bbd40cc..860bccb9b 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -74,7 +74,7 @@ namespace Jellyfin.Api.Controllers
result = result.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
}
- if (controllableByUserId.HasValue && !controllableByUserId.Equals(Guid.Empty))
+ if (controllableByUserId.HasValue && !controllableByUserId.Equals(default))
{
result = result.Where(i => i.SupportsRemoteControl);
@@ -82,12 +82,12 @@ namespace Jellyfin.Api.Controllers
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
{
- result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(controllableByUserId.Value));
+ result = result.Where(i => i.UserId.Equals(default) || i.ContainsUser(controllableByUserId.Value));
}
if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
{
- result = result.Where(i => !i.UserId.Equals(Guid.Empty));
+ result = result.Where(i => !i.UserId.Equals(default));
}
if (activeWithinSeconds.HasValue && activeWithinSeconds.Value > 0)
diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs
index 4422ef32c..053c7baaa 100644
--- a/Jellyfin.Api/Controllers/StudiosController.cs
+++ b/Jellyfin.Api/Controllers/StudiosController.cs
@@ -91,7 +91,9 @@ namespace Jellyfin.Api.Controllers
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
- User? user = userId.HasValue && userId != Guid.Empty ? _userManager.GetUserById(userId.Value) : null;
+ User? user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var parentItem = _libraryManager.GetParentItem(parentId, userId);
@@ -141,7 +143,7 @@ namespace Jellyfin.Api.Controllers
var dtoOptions = new DtoOptions().AddClientFields(Request);
var item = _libraryManager.GetStudio(name);
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Equals(default))
{
var user = _userManager.GetUserById(userId.Value);
diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs
index 73be26bb2..e9c46dcf3 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -63,7 +63,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit,
[FromQuery] bool enableTotalRecordCount = false)
{
- var user = !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId) : null;
+ var user = userId.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId);
var dtoOptions = new DtoOptions().AddClientFields(Request);
var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index 5cb7468b2..cf812fa23 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Enums;
@@ -31,7 +32,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Finds movies and trailers similar to a given trailer.
/// </summary>
- /// <param name="userId">The user id.</param>
+ /// <param name="userId">The user id supplied as query parameter; this is required when not using an API key.</param>
/// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
/// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
/// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
@@ -57,6 +58,11 @@ namespace Jellyfin.Api.Controllers
/// <param name="hasImdbId">Optional filter by items that have an imdb id or not.</param>
/// <param name="hasTmdbId">Optional filter by items that have a tmdb id or not.</param>
/// <param name="hasTvdbId">Optional filter by items that have a tvdb id or not.</param>
+ /// <param name="isMovie">Optional filter for live tv movies.</param>
+ /// <param name="isSeries">Optional filter for live tv series.</param>
+ /// <param name="isNews">Optional filter for live tv news.</param>
+ /// <param name="isKids">Optional filter for live tv kids.</param>
+ /// <param name="isSports">Optional filter for live tv sports.</param>
/// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
@@ -113,15 +119,15 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the trailers.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<QueryResult<BaseItemDto>> GetTrailers(
- [FromQuery] Guid userId,
+ public Task<ActionResult<QueryResult<BaseItemDto>>> GetTrailers(
+ [FromQuery] Guid? userId,
[FromQuery] string? maxOfficialRating,
[FromQuery] bool? hasThemeSong,
[FromQuery] bool? hasThemeVideo,
[FromQuery] bool? hasSubtitles,
[FromQuery] bool? hasSpecialFeature,
[FromQuery] bool? hasTrailer,
- [FromQuery] string? adjacentTo,
+ [FromQuery] Guid? adjacentTo,
[FromQuery] int? parentIndexNumber,
[FromQuery] bool? hasParentalRating,
[FromQuery] bool? isHd,
@@ -140,6 +146,11 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? hasImdbId,
[FromQuery] bool? hasTmdbId,
[FromQuery] bool? hasTvdbId,
+ [FromQuery] bool? isMovie,
+ [FromQuery] bool? isSeries,
+ [FromQuery] bool? isNews,
+ [FromQuery] bool? isKids,
+ [FromQuery] bool? isSports,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
@@ -224,6 +235,11 @@ namespace Jellyfin.Api.Controllers
hasImdbId,
hasTmdbId,
hasTvdbId,
+ isMovie,
+ isSeries,
+ isNews,
+ isKids,
+ isSports,
excludeItemIds,
startIndex,
limit,
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index 636130543..e39d05a6f 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -77,7 +77,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
- [FromQuery] string? seriesId,
+ [FromQuery] Guid? seriesId,
[FromQuery] Guid? parentId,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
@@ -107,9 +107,9 @@ namespace Jellyfin.Api.Controllers
},
options);
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user);
@@ -145,9 +145,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery] bool? enableUserData)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
var minPremiereDate = DateTime.UtcNow.Date.AddDays(-1);
@@ -206,7 +206,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? season,
[FromQuery] Guid? seasonId,
[FromQuery] bool? isMissing,
- [FromQuery] string? adjacentTo,
+ [FromQuery] Guid? adjacentTo,
[FromQuery] Guid? startItemId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
@@ -216,9 +216,9 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableUserData,
[FromQuery] string? sortBy)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
List<BaseItem> episodes;
@@ -278,9 +278,9 @@ namespace Jellyfin.Api.Controllers
}
// This must be the last filter
- if (!string.IsNullOrEmpty(adjacentTo))
+ if (adjacentTo.HasValue && !adjacentTo.Value.Equals(default))
{
- episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo).ToList();
+ episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo.Value).ToList();
}
if (string.Equals(sortBy, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
@@ -326,15 +326,15 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? isSpecialSeason,
[FromQuery] bool? isMissing,
- [FromQuery] string? adjacentTo,
+ [FromQuery] Guid? adjacentTo,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery] bool? enableUserData)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
if (_libraryManager.GetItemById(seriesId) is not Series series)
{
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index bc9527a0b..6fcafd426 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -223,7 +224,7 @@ namespace Jellyfin.Api.Controllers
DeInterlace = false,
RequireNonAnamorphic = false,
EnableMpegtsM2TsMode = false,
- TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(',', mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()),
+ TranscodeReasons = mediaSource.TranscodeReasons == 0 ? null : mediaSource.TranscodeReasons.ToString(),
Context = EncodingContext.Static,
StreamOptions = new Dictionary<string, string>(),
EnableAdaptiveBitrateStreaming = true
@@ -254,7 +255,7 @@ namespace Jellyfin.Api.Controllers
CopyTimestamps = true,
StartTimeTicks = startTimeTicks,
SubtitleMethod = SubtitleDeliveryMethod.Embed,
- TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(',', mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()),
+ TranscodeReasons = mediaSource.TranscodeReasons == 0 ? null : mediaSource.TranscodeReasons.ToString(),
Context = EncodingContext.Static
};
diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs
index 4263d4fe5..d1109bebc 100644
--- a/Jellyfin.Api/Controllers/UserController.cs
+++ b/Jellyfin.Api/Controllers/UserController.cs
@@ -282,16 +282,19 @@ namespace Jellyfin.Api.Controllers
}
else
{
- var success = await _userManager.AuthenticateUser(
- user.Username,
- request.CurrentPw,
- request.CurrentPw,
- HttpContext.GetNormalizedRemoteIp().ToString(),
- false).ConfigureAwait(false);
-
- if (success == null)
+ if (!HttpContext.User.IsInRole(UserRoles.Administrator))
{
- return StatusCode(StatusCodes.Status403Forbidden, "Invalid user or password entered.");
+ var success = await _userManager.AuthenticateUser(
+ user.Username,
+ request.CurrentPw,
+ request.CurrentPw,
+ HttpContext.GetNormalizedRemoteIp().ToString(),
+ false).ConfigureAwait(false);
+
+ if (success == null)
+ {
+ return StatusCode(StatusCodes.Status403Forbidden, "Invalid user or password entered.");
+ }
}
await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false);
@@ -499,7 +502,7 @@ namespace Jellyfin.Api.Controllers
if (isLocal)
{
- _logger.LogWarning("Password reset proccess initiated from outside the local network with IP: {IP}", ip);
+ _logger.LogWarning("Password reset process initiated from outside the local network with IP: {IP}", ip);
}
var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false);
@@ -534,7 +537,7 @@ namespace Jellyfin.Api.Controllers
public ActionResult<UserDto> GetCurrentUser()
{
var userId = ClaimHelpers.GetUserId(Request.HttpContext.User);
- if (userId == null)
+ if (userId is null)
{
return BadRequest();
}
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index 008d2f176..e45f9b58c 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.GetUserById(userId);
- var item = itemId.Equals(Guid.Empty)
+ var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -116,7 +116,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.GetUserById(userId);
- var item = itemId.Equals(Guid.Empty)
+ var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -197,7 +197,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.GetUserById(userId);
- var item = itemId.Equals(Guid.Empty)
+ var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -227,7 +227,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.GetUserById(userId);
- var item = itemId.Equals(Guid.Empty)
+ var item = itemId.Equals(default)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(itemId);
@@ -347,7 +347,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.GetUserById(userId);
- var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
+ var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
// Get the user data for this item
var data = _userDataRepository.GetUserData(user, item);
@@ -370,7 +370,7 @@ namespace Jellyfin.Api.Controllers
{
var user = _userManager.GetUserById(userId);
- var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
+ var item = itemId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
// Get the user data for this item
var data = _userDataRepository.GetUserData(user, item);
diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs
index 04171da8a..5cc8c906f 100644
--- a/Jellyfin.Api/Controllers/UserViewsController.cs
+++ b/Jellyfin.Api/Controllers/UserViewsController.cs
@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.UserViewDtos;
@@ -15,6 +16,7 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -24,6 +26,7 @@ namespace Jellyfin.Api.Controllers
/// User views controller.
/// </summary>
[Route("")]
+ [Authorize(Policy = Policies.DefaultAuthorization)]
public class UserViewsController : BaseJellyfinApiController
{
private readonly IUserManager _userManager;
@@ -65,7 +68,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="OkResult"/> containing the user views.</returns>
[HttpGet("Users/{userId}/Views")]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<QueryResult<BaseItemDto>>> GetUserViews(
+ public QueryResult<BaseItemDto> GetUserViews(
[FromRoute, Required] Guid userId,
[FromQuery] bool? includeExternalContent,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] presetViews,
@@ -87,12 +90,6 @@ namespace Jellyfin.Api.Controllers
query.PresetViews = presetViews;
}
- var app = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Client ?? string.Empty;
- if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
- {
- query.PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows };
- }
-
var folders = _userViewManager.GetUserViews(query);
var dtoOptions = new DtoOptions().AddClientFields(Request);
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index 44263fd98..4e2895934 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -109,14 +109,14 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId)
{
- var user = userId.HasValue && !userId.Equals(Guid.Empty)
- ? _userManager.GetUserById(userId.Value)
- : null;
-
- var item = itemId.Equals(Guid.Empty)
- ? (!userId.Equals(Guid.Empty)
- ? _libraryManager.GetUserRootFolder()
- : _libraryManager.RootFolder)
+ var user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
+
+ var item = itemId.Equals(default)
+ ? (userId is null || userId.Value.Equals(default)
+ ? _libraryManager.RootFolder
+ : _libraryManager.GetUserRootFolder())
: _libraryManager.GetItemById(itemId);
var dtoOptions = new DtoOptions();
@@ -221,7 +221,7 @@ namespace Jellyfin.Api.Controllers
var alternateVersionsOfPrimary = primaryVersion.LinkedAlternateVersions.ToList();
- foreach (var item in items.Where(i => i.Id != primaryVersion.Id))
+ foreach (var item in items.Where(i => !i.Id.Equals(primaryVersion.Id)))
{
item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture));
@@ -427,7 +427,7 @@ namespace Jellyfin.Api.Controllers
StreamOptions = streamOptions
};
- using var state = await StreamingHelpers.GetStreamingState(
+ var state = await StreamingHelpers.GetStreamingState(
streamingRequest,
Request,
_authContext,
diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs
index bac77d43b..7c02e2550 100644
--- a/Jellyfin.Api/Controllers/YearsController.cs
+++ b/Jellyfin.Api/Controllers/YearsController.cs
@@ -90,16 +90,11 @@ namespace Jellyfin.Api.Controllers
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
- User? user = null;
+ User? user = userId is null || userId.Value.Equals(default)
+ ? null
+ : _userManager.GetUserById(userId.Value);
BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
- if (userId.HasValue && !userId.Equals(Guid.Empty))
- {
- user = _userManager.GetUserById(userId.Value);
- }
-
- IList<BaseItem> items;
-
var query = new InternalItemsQuery(user)
{
ExcludeItemTypes = excludeItemTypes,
@@ -110,17 +105,18 @@ namespace Jellyfin.Api.Controllers
bool Filter(BaseItem i) => FilterItem(i, excludeItemTypes, includeItemTypes, mediaTypes);
+ IList<BaseItem> items;
if (parentItem.IsFolder)
{
var folder = (Folder)parentItem;
- if (!userId.Equals(Guid.Empty))
+ if (userId.Equals(default))
{
- items = recursive ? folder.GetRecursiveChildren(user, query).ToList() : folder.GetChildren(user, true).Where(Filter).ToList();
+ items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToList();
}
else
{
- items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToList();
+ items = recursive ? folder.GetRecursiveChildren(user, query).ToList() : folder.GetChildren(user, true).Where(Filter).ToList();
}
}
else
@@ -185,7 +181,7 @@ namespace Jellyfin.Api.Controllers
var dtoOptions = new DtoOptions()
.AddClientFields(Request);
- if (userId.HasValue && !userId.Equals(Guid.Empty))
+ if (userId.HasValue && !userId.Value.Equals(default))
{
var user = _userManager.GetUserById(userId.Value);
return _dtoService.GetBaseItemDto(item, dtoOptions, user);