Remove api client generator errors
This commit is contained in:
parent
7caf1662ec
commit
547ee88561
|
@ -85,15 +85,178 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="streamOptions">Optional. The streaming options.</param>
|
/// <param name="streamOptions">Optional. The streaming options.</param>
|
||||||
/// <response code="200">Audio stream returned.</response>
|
/// <response code="200">Audio stream returned.</response>
|
||||||
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
||||||
[HttpGet("{itemId}/stream.{container:required}", Name = "GetAudioStreamByContainer")]
|
|
||||||
[HttpGet("{itemId}/stream", Name = "GetAudioStream")]
|
[HttpGet("{itemId}/stream", Name = "GetAudioStream")]
|
||||||
[HttpHead("{itemId}/stream.{container:required}", Name = "HeadAudioStreamByContainer")]
|
|
||||||
[HttpHead("{itemId}/stream", Name = "HeadAudioStream")]
|
[HttpHead("{itemId}/stream", Name = "HeadAudioStream")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesAudioFile]
|
[ProducesAudioFile]
|
||||||
public async Task<ActionResult> GetAudioStream(
|
public async Task<ActionResult> GetAudioStream(
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromRoute] string? container,
|
[FromQuery] string? container,
|
||||||
|
[FromQuery] bool? @static,
|
||||||
|
[FromQuery] string? @params,
|
||||||
|
[FromQuery] string? tag,
|
||||||
|
[FromQuery] string? deviceProfileId,
|
||||||
|
[FromQuery] string? playSessionId,
|
||||||
|
[FromQuery] string? segmentContainer,
|
||||||
|
[FromQuery] int? segmentLength,
|
||||||
|
[FromQuery] int? minSegments,
|
||||||
|
[FromQuery] string? mediaSourceId,
|
||||||
|
[FromQuery] string? deviceId,
|
||||||
|
[FromQuery] string? audioCodec,
|
||||||
|
[FromQuery] bool? enableAutoStreamCopy,
|
||||||
|
[FromQuery] bool? allowVideoStreamCopy,
|
||||||
|
[FromQuery] bool? allowAudioStreamCopy,
|
||||||
|
[FromQuery] bool? breakOnNonKeyFrames,
|
||||||
|
[FromQuery] int? audioSampleRate,
|
||||||
|
[FromQuery] int? maxAudioBitDepth,
|
||||||
|
[FromQuery] int? audioBitRate,
|
||||||
|
[FromQuery] int? audioChannels,
|
||||||
|
[FromQuery] int? maxAudioChannels,
|
||||||
|
[FromQuery] string? profile,
|
||||||
|
[FromQuery] string? level,
|
||||||
|
[FromQuery] float? framerate,
|
||||||
|
[FromQuery] float? maxFramerate,
|
||||||
|
[FromQuery] bool? copyTimestamps,
|
||||||
|
[FromQuery] long? startTimeTicks,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? videoBitRate,
|
||||||
|
[FromQuery] int? subtitleStreamIndex,
|
||||||
|
[FromQuery] SubtitleDeliveryMethod subtitleMethod,
|
||||||
|
[FromQuery] int? maxRefFrames,
|
||||||
|
[FromQuery] int? maxVideoBitDepth,
|
||||||
|
[FromQuery] bool? requireAvc,
|
||||||
|
[FromQuery] bool? deInterlace,
|
||||||
|
[FromQuery] bool? requireNonAnamorphic,
|
||||||
|
[FromQuery] int? transcodingMaxAudioChannels,
|
||||||
|
[FromQuery] int? cpuCoreLimit,
|
||||||
|
[FromQuery] string? liveStreamId,
|
||||||
|
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||||
|
[FromQuery] string? videoCodec,
|
||||||
|
[FromQuery] string? subtitleCodec,
|
||||||
|
[FromQuery] string? transcodingReasons,
|
||||||
|
[FromQuery] int? audioStreamIndex,
|
||||||
|
[FromQuery] int? videoStreamIndex,
|
||||||
|
[FromQuery] EncodingContext? context,
|
||||||
|
[FromQuery] Dictionary<string, string>? streamOptions)
|
||||||
|
{
|
||||||
|
StreamingRequestDto streamingRequest = new StreamingRequestDto
|
||||||
|
{
|
||||||
|
Id = itemId,
|
||||||
|
Container = container,
|
||||||
|
Static = @static ?? true,
|
||||||
|
Params = @params,
|
||||||
|
Tag = tag,
|
||||||
|
DeviceProfileId = deviceProfileId,
|
||||||
|
PlaySessionId = playSessionId,
|
||||||
|
SegmentContainer = segmentContainer,
|
||||||
|
SegmentLength = segmentLength,
|
||||||
|
MinSegments = minSegments,
|
||||||
|
MediaSourceId = mediaSourceId,
|
||||||
|
DeviceId = deviceId,
|
||||||
|
AudioCodec = audioCodec,
|
||||||
|
EnableAutoStreamCopy = enableAutoStreamCopy ?? true,
|
||||||
|
AllowAudioStreamCopy = allowAudioStreamCopy ?? true,
|
||||||
|
AllowVideoStreamCopy = allowVideoStreamCopy ?? true,
|
||||||
|
BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
|
||||||
|
AudioSampleRate = audioSampleRate,
|
||||||
|
MaxAudioChannels = maxAudioChannels,
|
||||||
|
AudioBitRate = audioBitRate,
|
||||||
|
MaxAudioBitDepth = maxAudioBitDepth,
|
||||||
|
AudioChannels = audioChannels,
|
||||||
|
Profile = profile,
|
||||||
|
Level = level,
|
||||||
|
Framerate = framerate,
|
||||||
|
MaxFramerate = maxFramerate,
|
||||||
|
CopyTimestamps = copyTimestamps ?? true,
|
||||||
|
StartTimeTicks = startTimeTicks,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
VideoBitRate = videoBitRate,
|
||||||
|
SubtitleStreamIndex = subtitleStreamIndex,
|
||||||
|
SubtitleMethod = subtitleMethod,
|
||||||
|
MaxRefFrames = maxRefFrames,
|
||||||
|
MaxVideoBitDepth = maxVideoBitDepth,
|
||||||
|
RequireAvc = requireAvc ?? true,
|
||||||
|
DeInterlace = deInterlace ?? true,
|
||||||
|
RequireNonAnamorphic = requireNonAnamorphic ?? true,
|
||||||
|
TranscodingMaxAudioChannels = transcodingMaxAudioChannels,
|
||||||
|
CpuCoreLimit = cpuCoreLimit,
|
||||||
|
LiveStreamId = liveStreamId,
|
||||||
|
EnableMpegtsM2TsMode = enableMpegtsM2TsMode ?? true,
|
||||||
|
VideoCodec = videoCodec,
|
||||||
|
SubtitleCodec = subtitleCodec,
|
||||||
|
TranscodeReasons = transcodingReasons,
|
||||||
|
AudioStreamIndex = audioStreamIndex,
|
||||||
|
VideoStreamIndex = videoStreamIndex,
|
||||||
|
Context = context ?? EncodingContext.Static,
|
||||||
|
StreamOptions = streamOptions
|
||||||
|
};
|
||||||
|
|
||||||
|
return await _audioHelper.GetAudioStream(_transcodingJobType, streamingRequest).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an audio stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item id.</param>
|
||||||
|
/// <param name="container">The audio container.</param>
|
||||||
|
/// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
|
||||||
|
/// <param name="params">The streaming parameters.</param>
|
||||||
|
/// <param name="tag">The tag.</param>
|
||||||
|
/// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
|
||||||
|
/// <param name="playSessionId">The play session id.</param>
|
||||||
|
/// <param name="segmentContainer">The segment container.</param>
|
||||||
|
/// <param name="segmentLength">The segment lenght.</param>
|
||||||
|
/// <param name="minSegments">The minimum number of segments.</param>
|
||||||
|
/// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
|
||||||
|
/// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
|
||||||
|
/// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
|
||||||
|
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
|
||||||
|
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
|
||||||
|
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
|
||||||
|
/// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
|
||||||
|
/// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
|
||||||
|
/// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
|
||||||
|
/// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
|
||||||
|
/// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
|
||||||
|
/// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
|
||||||
|
/// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
|
||||||
|
/// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
|
||||||
|
/// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
|
||||||
|
/// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
|
||||||
|
/// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
|
||||||
|
/// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
|
||||||
|
/// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
|
||||||
|
/// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
|
||||||
|
/// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
|
||||||
|
/// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
|
||||||
|
/// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
|
||||||
|
/// <param name="maxRefFrames">Optional.</param>
|
||||||
|
/// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
|
||||||
|
/// <param name="requireAvc">Optional. Whether to require avc.</param>
|
||||||
|
/// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
|
||||||
|
/// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
|
||||||
|
/// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
|
||||||
|
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
|
||||||
|
/// <param name="liveStreamId">The live stream id.</param>
|
||||||
|
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
|
||||||
|
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
|
||||||
|
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
|
||||||
|
/// <param name="transcodingReasons">Optional. The transcoding reason.</param>
|
||||||
|
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
|
||||||
|
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
|
||||||
|
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
|
||||||
|
/// <param name="streamOptions">Optional. The streaming options.</param>
|
||||||
|
/// <response code="200">Audio stream returned.</response>
|
||||||
|
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
||||||
|
[HttpGet("{itemId}/stream.{container}", Name = "GetAudioStreamByContainer")]
|
||||||
|
[HttpHead("{itemId}/stream.{container}", Name = "HeadAudioStreamByContainer")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesAudioFile]
|
||||||
|
public async Task<ActionResult> GetAudioStreamByContainer(
|
||||||
|
[FromRoute, Required] Guid itemId,
|
||||||
|
[FromRoute, Required] string container,
|
||||||
[FromQuery] bool? @static,
|
[FromQuery] bool? @static,
|
||||||
[FromQuery] string? @params,
|
[FromQuery] string? @params,
|
||||||
[FromQuery] string? tag,
|
[FromQuery] string? tag,
|
||||||
|
|
|
@ -833,7 +833,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromRoute, Required] string playlistId,
|
[FromRoute, Required] string playlistId,
|
||||||
[FromRoute, Required] int segmentId,
|
[FromRoute, Required] int segmentId,
|
||||||
[FromRoute] string container,
|
[FromRoute, Required] string container,
|
||||||
[FromQuery] bool? @static,
|
[FromQuery] bool? @static,
|
||||||
[FromQuery] string? @params,
|
[FromQuery] string? @params,
|
||||||
[FromQuery] string? tag,
|
[FromQuery] string? tag,
|
||||||
|
@ -1004,7 +1004,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromRoute, Required] string playlistId,
|
[FromRoute, Required] string playlistId,
|
||||||
[FromRoute, Required] int segmentId,
|
[FromRoute, Required] int segmentId,
|
||||||
[FromRoute] string container,
|
[FromRoute, Required] string container,
|
||||||
[FromQuery] bool? @static,
|
[FromQuery] bool? @static,
|
||||||
[FromQuery] string? @params,
|
[FromQuery] string? @params,
|
||||||
[FromQuery] string? tag,
|
[FromQuery] string? tag,
|
||||||
|
|
|
@ -85,7 +85,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="403">User does not have permission to delete the image.</response>
|
/// <response code="403">User does not have permission to delete the image.</response>
|
||||||
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
[HttpPost("Users/{userId}/Images/{imageType}")]
|
[HttpPost("Users/{userId}/Images/{imageType}")]
|
||||||
[HttpPost("Users/{userId}/Images/{imageType}/{index?}", Name = "PostUserImage_2")]
|
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
|
@ -94,7 +93,53 @@ namespace Jellyfin.Api.Controllers
|
||||||
public async Task<ActionResult> PostUserImage(
|
public async Task<ActionResult> PostUserImage(
|
||||||
[FromRoute, Required] Guid userId,
|
[FromRoute, Required] Guid userId,
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromRoute] int? index = null)
|
[FromQuery] int? index = null)
|
||||||
|
{
|
||||||
|
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
|
||||||
|
{
|
||||||
|
return Forbid("User is not allowed to update the image.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = _userManager.GetUserById(userId);
|
||||||
|
await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Handle image/png; charset=utf-8
|
||||||
|
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
|
||||||
|
var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
|
||||||
|
if (user.ProfileImage != null)
|
||||||
|
{
|
||||||
|
await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
|
||||||
|
|
||||||
|
await _providerManager
|
||||||
|
.SaveImage(memoryStream, mimeType, user.ProfileImage.Path)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the user image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">User Id.</param>
|
||||||
|
/// <param name="imageType">(Unused) Image type.</param>
|
||||||
|
/// <param name="index">(Unused) Image index.</param>
|
||||||
|
/// <response code="204">Image updated.</response>
|
||||||
|
/// <response code="403">User does not have permission to delete the image.</response>
|
||||||
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
|
[HttpPost("Users/{userId}/Images/{imageType}/{index}")]
|
||||||
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
|
public async Task<ActionResult> PostUserImageByIndex(
|
||||||
|
[FromRoute, Required] Guid userId,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute] int index)
|
||||||
{
|
{
|
||||||
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
|
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
|
||||||
{
|
{
|
||||||
|
@ -131,8 +176,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="204">Image deleted.</response>
|
/// <response code="204">Image deleted.</response>
|
||||||
/// <response code="403">User does not have permission to delete the image.</response>
|
/// <response code="403">User does not have permission to delete the image.</response>
|
||||||
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
[HttpDelete("Users/{userId}/Images/{itemType}")]
|
[HttpDelete("Users/{userId}/Images/{imageType}")]
|
||||||
[HttpDelete("Users/{userId}/Images/{itemType}/{index?}", Name = "DeleteUserImage_2")]
|
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
|
@ -141,7 +185,46 @@ namespace Jellyfin.Api.Controllers
|
||||||
public async Task<ActionResult> DeleteUserImage(
|
public async Task<ActionResult> DeleteUserImage(
|
||||||
[FromRoute, Required] Guid userId,
|
[FromRoute, Required] Guid userId,
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromRoute] int? index = null)
|
[FromQuery] int? index = null)
|
||||||
|
{
|
||||||
|
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
|
||||||
|
{
|
||||||
|
return Forbid("User is not allowed to delete the image.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = _userManager.GetUserById(userId);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(user.ProfileImage.Path);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Error deleting user profile image:");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete the user's image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">User Id.</param>
|
||||||
|
/// <param name="imageType">(Unused) Image type.</param>
|
||||||
|
/// <param name="index">(Unused) Image index.</param>
|
||||||
|
/// <response code="204">Image deleted.</response>
|
||||||
|
/// <response code="403">User does not have permission to delete the image.</response>
|
||||||
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
|
[HttpDelete("Users/{userId}/Images/{imageType}/{index}")]
|
||||||
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
|
public async Task<ActionResult> DeleteUserImageByIndex(
|
||||||
|
[FromRoute, Required] Guid userId,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute] int index)
|
||||||
{
|
{
|
||||||
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
|
if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true))
|
||||||
{
|
{
|
||||||
|
@ -172,14 +255,13 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="404">Item not found.</response>
|
/// <response code="404">Item not found.</response>
|
||||||
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
||||||
[HttpDelete("Items/{itemId}/Images/{imageType}")]
|
[HttpDelete("Items/{itemId}/Images/{imageType}")]
|
||||||
[HttpDelete("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "DeleteItemImage_2")]
|
|
||||||
[Authorize(Policy = Policies.RequiresElevation)]
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> DeleteItemImage(
|
public async Task<ActionResult> DeleteItemImage(
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -191,6 +273,65 @@ namespace Jellyfin.Api.Controllers
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete an item's image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">Item id.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">The image index.</param>
|
||||||
|
/// <response code="204">Image deleted.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
||||||
|
[HttpDelete("Items/{itemId}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult> DeleteItemImageByIndex(
|
||||||
|
[FromRoute, Required] Guid itemId,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute] int imageIndex)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
await item.DeleteImageAsync(imageType, imageIndex).ConfigureAwait(false);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set item image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">Item id.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <response code="204">Image saved.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
||||||
|
[HttpPost("Items/{itemId}/Images/{imageType}")]
|
||||||
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
|
public async Task<ActionResult> SetItemImage(
|
||||||
|
[FromRoute, Required] Guid itemId,
|
||||||
|
[FromRoute, Required] ImageType imageType)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle image/png; charset=utf-8
|
||||||
|
var mimeType = Request.ContentType.Split(';').FirstOrDefault();
|
||||||
|
await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set item image.
|
/// Set item image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -200,16 +341,15 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="204">Image saved.</response>
|
/// <response code="204">Image saved.</response>
|
||||||
/// <response code="404">Item not found.</response>
|
/// <response code="404">Item not found.</response>
|
||||||
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
||||||
[HttpPost("Items/{itemId}/Images/{imageType}")]
|
[HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex}")]
|
||||||
[HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "SetItemImage_2")]
|
|
||||||
[Authorize(Policy = Policies.RequiresElevation)]
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
public async Task<ActionResult> SetItemImage(
|
public async Task<ActionResult> SetItemImageByIndex(
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromRoute] int imageIndex)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -349,8 +489,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("Items/{itemId}/Images/{imageType}")]
|
[HttpGet("Items/{itemId}/Images/{imageType}")]
|
||||||
[HttpHead("Items/{itemId}/Images/{imageType}", Name = "HeadItemImage")]
|
[HttpHead("Items/{itemId}/Images/{imageType}", Name = "HeadItemImage")]
|
||||||
[HttpGet("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "GetItemImage_2")]
|
|
||||||
[HttpHead("Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "HeadItemImage_2")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
|
@ -371,7 +509,86 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? blur,
|
[FromQuery] int? blur,
|
||||||
[FromQuery] string? backgroundColor,
|
[FromQuery] string? backgroundColor,
|
||||||
[FromQuery] string? foregroundLayer,
|
[FromQuery] string? foregroundLayer,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetImageInternal(
|
||||||
|
itemId,
|
||||||
|
imageType,
|
||||||
|
imageIndex,
|
||||||
|
tag,
|
||||||
|
format,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
percentPlayed,
|
||||||
|
unplayedCount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
cropWhitespace,
|
||||||
|
addPlayedIndicator,
|
||||||
|
blur,
|
||||||
|
backgroundColor,
|
||||||
|
foregroundLayer,
|
||||||
|
item,
|
||||||
|
Request.Method.Equals(HttpMethods.Head, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item's image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">Item id.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">Image index.</param>
|
||||||
|
/// <param name="maxWidth">The maximum image width to return.</param>
|
||||||
|
/// <param name="maxHeight">The maximum image height to return.</param>
|
||||||
|
/// <param name="width">The fixed image width to return.</param>
|
||||||
|
/// <param name="height">The fixed image height to return.</param>
|
||||||
|
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
|
||||||
|
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
|
||||||
|
/// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
|
||||||
|
/// <param name="format">Optional. The <see cref="ImageFormat"/> of the returned image.</param>
|
||||||
|
/// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
|
||||||
|
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
|
||||||
|
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
|
||||||
|
/// <param name="blur">Optional. Blur image.</param>
|
||||||
|
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
|
||||||
|
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
|
||||||
|
/// <response code="200">Image stream returned.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
|
/// </returns>
|
||||||
|
[HttpGet("Items/{itemId}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[HttpHead("Items/{itemId}/Images/{imageType}/{imageIndex}", Name = "HeadItemImageByIndex")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesImageFile]
|
||||||
|
public async Task<ActionResult> GetItemImageByIndex(
|
||||||
|
[FromRoute, Required] Guid itemId,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute] int imageIndex,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? quality,
|
||||||
|
[FromQuery] string? tag,
|
||||||
|
[FromQuery] bool? cropWhitespace,
|
||||||
|
[FromQuery] ImageFormat? format,
|
||||||
|
[FromQuery] bool? addPlayedIndicator,
|
||||||
|
[FromQuery] double? percentPlayed,
|
||||||
|
[FromQuery] int? unplayedCount,
|
||||||
|
[FromQuery] int? blur,
|
||||||
|
[FromQuery] string? backgroundColor,
|
||||||
|
[FromQuery] string? foregroundLayer)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -507,8 +724,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
/// or a <see cref="NotFoundResult"/> if item not found.
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("Artists/{name}/Images/{imageType}/{imageIndex?}")]
|
[HttpGet("Artists/{name}/Images/{imageType}/{imageIndex}")]
|
||||||
[HttpHead("Artists/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadArtistImage")]
|
[HttpHead("Artists/{name}/Images/{imageType}/{imageIndex}", Name = "HeadArtistImage")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
|
@ -586,8 +803,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
/// or a <see cref="NotFoundResult"/> if item not found.
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("Genres/{name}/Images/{imageType}/{imageIndex?}")]
|
[HttpGet("Genres/{name}/Images/{imageType}")]
|
||||||
[HttpHead("Genres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadGenreImage")]
|
[HttpHead("Genres/{name}/Images/{imageType}", Name = "HeadGenreImage")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
|
@ -608,7 +825,86 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? blur,
|
[FromQuery] int? blur,
|
||||||
[FromQuery] string? backgroundColor,
|
[FromQuery] string? backgroundColor,
|
||||||
[FromQuery] string? foregroundLayer,
|
[FromQuery] string? foregroundLayer,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetGenre(name);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetImageInternal(
|
||||||
|
item.Id,
|
||||||
|
imageType,
|
||||||
|
imageIndex,
|
||||||
|
tag,
|
||||||
|
format,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
percentPlayed,
|
||||||
|
unplayedCount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
cropWhitespace,
|
||||||
|
addPlayedIndicator,
|
||||||
|
blur,
|
||||||
|
backgroundColor,
|
||||||
|
foregroundLayer,
|
||||||
|
item,
|
||||||
|
Request.Method.Equals(HttpMethods.Head, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get genre image by name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Genre name.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">Image index.</param>
|
||||||
|
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
|
||||||
|
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
|
||||||
|
/// <param name="maxWidth">The maximum image width to return.</param>
|
||||||
|
/// <param name="maxHeight">The maximum image height to return.</param>
|
||||||
|
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
|
||||||
|
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
|
||||||
|
/// <param name="width">The fixed image width to return.</param>
|
||||||
|
/// <param name="height">The fixed image height to return.</param>
|
||||||
|
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
|
||||||
|
/// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
|
||||||
|
/// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
|
||||||
|
/// <param name="blur">Optional. Blur image.</param>
|
||||||
|
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
|
||||||
|
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
|
||||||
|
/// <response code="200">Image stream returned.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
|
/// </returns>
|
||||||
|
[HttpGet("Genres/{name}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[HttpHead("Genres/{name}/Images/{imageType}/{imageIndex}", Name = "HeadGenreImageByIndex")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesImageFile]
|
||||||
|
public async Task<ActionResult> GetGenreImageByIndex(
|
||||||
|
[FromRoute, Required] string name,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute, Required] int imageIndex,
|
||||||
|
[FromQuery] string tag,
|
||||||
|
[FromQuery] ImageFormat? format,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] double? percentPlayed,
|
||||||
|
[FromQuery] int? unplayedCount,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? quality,
|
||||||
|
[FromQuery] bool? cropWhitespace,
|
||||||
|
[FromQuery] bool? addPlayedIndicator,
|
||||||
|
[FromQuery] int? blur,
|
||||||
|
[FromQuery] string? backgroundColor,
|
||||||
|
[FromQuery] string? foregroundLayer)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetGenre(name);
|
var item = _libraryManager.GetGenre(name);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -665,8 +961,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
/// or a <see cref="NotFoundResult"/> if item not found.
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("MusicGenres/{name}/Images/{imageType}/{imageIndex?}")]
|
[HttpGet("MusicGenres/{name}/Images/{imageType}")]
|
||||||
[HttpHead("MusicGenres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadMusicGenreImage")]
|
[HttpHead("MusicGenres/{name}/Images/{imageType}", Name = "HeadMusicGenreImage")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
|
@ -687,7 +983,86 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? blur,
|
[FromQuery] int? blur,
|
||||||
[FromQuery] string? backgroundColor,
|
[FromQuery] string? backgroundColor,
|
||||||
[FromQuery] string? foregroundLayer,
|
[FromQuery] string? foregroundLayer,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetMusicGenre(name);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetImageInternal(
|
||||||
|
item.Id,
|
||||||
|
imageType,
|
||||||
|
imageIndex,
|
||||||
|
tag,
|
||||||
|
format,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
percentPlayed,
|
||||||
|
unplayedCount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
cropWhitespace,
|
||||||
|
addPlayedIndicator,
|
||||||
|
blur,
|
||||||
|
backgroundColor,
|
||||||
|
foregroundLayer,
|
||||||
|
item,
|
||||||
|
Request.Method.Equals(HttpMethods.Head, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get music genre image by name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Music genre name.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">Image index.</param>
|
||||||
|
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
|
||||||
|
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
|
||||||
|
/// <param name="maxWidth">The maximum image width to return.</param>
|
||||||
|
/// <param name="maxHeight">The maximum image height to return.</param>
|
||||||
|
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
|
||||||
|
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
|
||||||
|
/// <param name="width">The fixed image width to return.</param>
|
||||||
|
/// <param name="height">The fixed image height to return.</param>
|
||||||
|
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
|
||||||
|
/// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
|
||||||
|
/// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
|
||||||
|
/// <param name="blur">Optional. Blur image.</param>
|
||||||
|
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
|
||||||
|
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
|
||||||
|
/// <response code="200">Image stream returned.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
|
/// </returns>
|
||||||
|
[HttpGet("MusicGenres/{name}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[HttpHead("MusicGenres/{name}/Images/{imageType}/{imageIndex}", Name = "HeadMusicGenreImageByIndex")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesImageFile]
|
||||||
|
public async Task<ActionResult> GetMusicGenreImageByIndex(
|
||||||
|
[FromRoute, Required] string name,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute, Required] int imageIndex,
|
||||||
|
[FromQuery] string tag,
|
||||||
|
[FromQuery] ImageFormat? format,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] double? percentPlayed,
|
||||||
|
[FromQuery] int? unplayedCount,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? quality,
|
||||||
|
[FromQuery] bool? cropWhitespace,
|
||||||
|
[FromQuery] bool? addPlayedIndicator,
|
||||||
|
[FromQuery] int? blur,
|
||||||
|
[FromQuery] string? backgroundColor,
|
||||||
|
[FromQuery] string? foregroundLayer)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetMusicGenre(name);
|
var item = _libraryManager.GetMusicGenre(name);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -744,8 +1119,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
/// or a <see cref="NotFoundResult"/> if item not found.
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("Persons/{name}/Images/{imageType}/{imageIndex?}")]
|
[HttpGet("Persons/{name}/Images/{imageType}")]
|
||||||
[HttpHead("Persons/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadPersonImage")]
|
[HttpHead("Persons/{name}/Images/{imageType}", Name = "HeadPersonImage")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
|
@ -766,7 +1141,86 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? blur,
|
[FromQuery] int? blur,
|
||||||
[FromQuery] string? backgroundColor,
|
[FromQuery] string? backgroundColor,
|
||||||
[FromQuery] string? foregroundLayer,
|
[FromQuery] string? foregroundLayer,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetPerson(name);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetImageInternal(
|
||||||
|
item.Id,
|
||||||
|
imageType,
|
||||||
|
imageIndex,
|
||||||
|
tag,
|
||||||
|
format,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
percentPlayed,
|
||||||
|
unplayedCount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
cropWhitespace,
|
||||||
|
addPlayedIndicator,
|
||||||
|
blur,
|
||||||
|
backgroundColor,
|
||||||
|
foregroundLayer,
|
||||||
|
item,
|
||||||
|
Request.Method.Equals(HttpMethods.Head, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get person image by name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Person name.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">Image index.</param>
|
||||||
|
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
|
||||||
|
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
|
||||||
|
/// <param name="maxWidth">The maximum image width to return.</param>
|
||||||
|
/// <param name="maxHeight">The maximum image height to return.</param>
|
||||||
|
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
|
||||||
|
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
|
||||||
|
/// <param name="width">The fixed image width to return.</param>
|
||||||
|
/// <param name="height">The fixed image height to return.</param>
|
||||||
|
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
|
||||||
|
/// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
|
||||||
|
/// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
|
||||||
|
/// <param name="blur">Optional. Blur image.</param>
|
||||||
|
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
|
||||||
|
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
|
||||||
|
/// <response code="200">Image stream returned.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
|
/// </returns>
|
||||||
|
[HttpGet("Persons/{name}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[HttpHead("Persons/{name}/Images/{imageType}/{imageIndex}", Name = "HeadPersonImageByIndex")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesImageFile]
|
||||||
|
public async Task<ActionResult> GetPersonImageByIndex(
|
||||||
|
[FromRoute, Required] string name,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute, Required] int imageIndex,
|
||||||
|
[FromQuery] string tag,
|
||||||
|
[FromQuery] ImageFormat? format,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] double? percentPlayed,
|
||||||
|
[FromQuery] int? unplayedCount,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? quality,
|
||||||
|
[FromQuery] bool? cropWhitespace,
|
||||||
|
[FromQuery] bool? addPlayedIndicator,
|
||||||
|
[FromQuery] int? blur,
|
||||||
|
[FromQuery] string? backgroundColor,
|
||||||
|
[FromQuery] string? foregroundLayer)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetPerson(name);
|
var item = _libraryManager.GetPerson(name);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -823,16 +1277,16 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
/// or a <see cref="NotFoundResult"/> if item not found.
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("Studios/{name}/Images/{imageType}/{imageIndex?}")]
|
[HttpGet("Studios/{name}/Images/{imageType}")]
|
||||||
[HttpHead("Studios/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadStudioImage")]
|
[HttpHead("Studios/{name}/Images/{imageType}", Name = "HeadStudioImage")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
public async Task<ActionResult> GetStudioImage(
|
public async Task<ActionResult> GetStudioImage(
|
||||||
[FromRoute, Required] string name,
|
[FromRoute, Required] string name,
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromRoute, Required] string tag,
|
[FromQuery] string? tag,
|
||||||
[FromRoute, Required] ImageFormat format,
|
[FromQuery] ImageFormat? format,
|
||||||
[FromQuery] int? maxWidth,
|
[FromQuery] int? maxWidth,
|
||||||
[FromQuery] int? maxHeight,
|
[FromQuery] int? maxHeight,
|
||||||
[FromQuery] double? percentPlayed,
|
[FromQuery] double? percentPlayed,
|
||||||
|
@ -845,7 +1299,86 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? blur,
|
[FromQuery] int? blur,
|
||||||
[FromQuery] string? backgroundColor,
|
[FromQuery] string? backgroundColor,
|
||||||
[FromQuery] string? foregroundLayer,
|
[FromQuery] string? foregroundLayer,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
|
{
|
||||||
|
var item = _libraryManager.GetStudio(name);
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetImageInternal(
|
||||||
|
item.Id,
|
||||||
|
imageType,
|
||||||
|
imageIndex,
|
||||||
|
tag,
|
||||||
|
format,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
percentPlayed,
|
||||||
|
unplayedCount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
cropWhitespace,
|
||||||
|
addPlayedIndicator,
|
||||||
|
blur,
|
||||||
|
backgroundColor,
|
||||||
|
foregroundLayer,
|
||||||
|
item,
|
||||||
|
Request.Method.Equals(HttpMethods.Head, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get studio image by name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Studio name.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">Image index.</param>
|
||||||
|
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
|
||||||
|
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
|
||||||
|
/// <param name="maxWidth">The maximum image width to return.</param>
|
||||||
|
/// <param name="maxHeight">The maximum image height to return.</param>
|
||||||
|
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
|
||||||
|
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
|
||||||
|
/// <param name="width">The fixed image width to return.</param>
|
||||||
|
/// <param name="height">The fixed image height to return.</param>
|
||||||
|
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
|
||||||
|
/// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
|
||||||
|
/// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
|
||||||
|
/// <param name="blur">Optional. Blur image.</param>
|
||||||
|
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
|
||||||
|
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
|
||||||
|
/// <response code="200">Image stream returned.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
|
/// </returns>
|
||||||
|
[HttpGet("Studios/{name}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[HttpHead("Studios/{name}/Images/{imageType}/{imageIndex}", Name = "HeadStudioImageByIndex")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesImageFile]
|
||||||
|
public async Task<ActionResult> GetStudioImageByIndex(
|
||||||
|
[FromRoute, Required] string name,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute, Required] int imageIndex,
|
||||||
|
[FromQuery] string? tag,
|
||||||
|
[FromQuery] ImageFormat? format,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] double? percentPlayed,
|
||||||
|
[FromQuery] int? unplayedCount,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? quality,
|
||||||
|
[FromQuery] bool? cropWhitespace,
|
||||||
|
[FromQuery] bool? addPlayedIndicator,
|
||||||
|
[FromQuery] int? blur,
|
||||||
|
[FromQuery] string? backgroundColor,
|
||||||
|
[FromQuery] string? foregroundLayer)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetStudio(name);
|
var item = _libraryManager.GetStudio(name);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
|
@ -902,8 +1435,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
/// or a <see cref="NotFoundResult"/> if item not found.
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
[HttpGet("Users/{userId}/Images/{imageType}/{imageIndex?}")]
|
[HttpGet("Users/{userId}/Images/{imageType}")]
|
||||||
[HttpHead("Users/{userId}/Images/{imageType}/{imageIndex?}", Name = "HeadUserImage")]
|
[HttpHead("Users/{userId}/Images/{imageType}", Name = "HeadUserImage")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[ProducesImageFile]
|
[ProducesImageFile]
|
||||||
|
@ -924,7 +1457,104 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? blur,
|
[FromQuery] int? blur,
|
||||||
[FromQuery] string? backgroundColor,
|
[FromQuery] string? backgroundColor,
|
||||||
[FromQuery] string? foregroundLayer,
|
[FromQuery] string? foregroundLayer,
|
||||||
[FromRoute] int? imageIndex = null)
|
[FromQuery] int? imageIndex)
|
||||||
|
{
|
||||||
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = new ItemImageInfo
|
||||||
|
{
|
||||||
|
Path = user.ProfileImage.Path,
|
||||||
|
Type = ImageType.Profile,
|
||||||
|
DateModified = user.ProfileImage.LastModified
|
||||||
|
};
|
||||||
|
|
||||||
|
if (width.HasValue)
|
||||||
|
{
|
||||||
|
info.Width = width.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height.HasValue)
|
||||||
|
{
|
||||||
|
info.Height = height.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetImageInternal(
|
||||||
|
user.Id,
|
||||||
|
imageType,
|
||||||
|
imageIndex,
|
||||||
|
tag,
|
||||||
|
format,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
percentPlayed,
|
||||||
|
unplayedCount,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quality,
|
||||||
|
cropWhitespace,
|
||||||
|
addPlayedIndicator,
|
||||||
|
blur,
|
||||||
|
backgroundColor,
|
||||||
|
foregroundLayer,
|
||||||
|
null,
|
||||||
|
Request.Method.Equals(HttpMethods.Head, StringComparison.OrdinalIgnoreCase),
|
||||||
|
info)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get user profile image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">User id.</param>
|
||||||
|
/// <param name="imageType">Image type.</param>
|
||||||
|
/// <param name="imageIndex">Image index.</param>
|
||||||
|
/// <param name="tag">Optional. Supply the cache tag from the item object to receive strong caching headers.</param>
|
||||||
|
/// <param name="format">Determines the output format of the image - original,gif,jpg,png.</param>
|
||||||
|
/// <param name="maxWidth">The maximum image width to return.</param>
|
||||||
|
/// <param name="maxHeight">The maximum image height to return.</param>
|
||||||
|
/// <param name="percentPlayed">Optional. Percent to render for the percent played overlay.</param>
|
||||||
|
/// <param name="unplayedCount">Optional. Unplayed count overlay to render.</param>
|
||||||
|
/// <param name="width">The fixed image width to return.</param>
|
||||||
|
/// <param name="height">The fixed image height to return.</param>
|
||||||
|
/// <param name="quality">Optional. Quality setting, from 0-100. Defaults to 90 and should suffice in most cases.</param>
|
||||||
|
/// <param name="cropWhitespace">Optional. Specify if whitespace should be cropped out of the image. True/False. If unspecified, whitespace will be cropped from logos and clear art.</param>
|
||||||
|
/// <param name="addPlayedIndicator">Optional. Add a played indicator.</param>
|
||||||
|
/// <param name="blur">Optional. Blur image.</param>
|
||||||
|
/// <param name="backgroundColor">Optional. Apply a background color for transparent images.</param>
|
||||||
|
/// <param name="foregroundLayer">Optional. Apply a foreground layer on top of the image.</param>
|
||||||
|
/// <response code="200">Image stream returned.</response>
|
||||||
|
/// <response code="404">Item not found.</response>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="FileStreamResult"/> containing the file stream on success,
|
||||||
|
/// or a <see cref="NotFoundResult"/> if item not found.
|
||||||
|
/// </returns>
|
||||||
|
[HttpGet("Users/{userId}/Images/{imageType}/{imageIndex}")]
|
||||||
|
[HttpHead("Users/{userId}/Images/{imageType}/{imageIndex}", Name = "HeadUserImageByIndex")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesImageFile]
|
||||||
|
public async Task<ActionResult> GetUserImageByIndex(
|
||||||
|
[FromRoute, Required] Guid userId,
|
||||||
|
[FromRoute, Required] ImageType imageType,
|
||||||
|
[FromRoute, Required] int imageIndex,
|
||||||
|
[FromQuery] string? tag,
|
||||||
|
[FromQuery] ImageFormat? format,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] double? percentPlayed,
|
||||||
|
[FromQuery] int? unplayedCount,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? quality,
|
||||||
|
[FromQuery] bool? cropWhitespace,
|
||||||
|
[FromQuery] bool? addPlayedIndicator,
|
||||||
|
[FromQuery] int? blur,
|
||||||
|
[FromQuery] string? backgroundColor,
|
||||||
|
[FromQuery] string? foregroundLayer)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
|
|
|
@ -206,7 +206,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
|
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
|
||||||
/// <response code="200">Instant playlist returned.</response>
|
/// <response code="200">Instant playlist returned.</response>
|
||||||
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
|
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
|
||||||
[HttpGet("Artists/InstantMix")]
|
[HttpGet("Artists/{id}/InstantMix")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromArtists(
|
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromArtists(
|
||||||
[FromRoute, Required] Guid id,
|
[FromRoute, Required] Guid id,
|
||||||
|
@ -242,7 +242,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
|
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
|
||||||
/// <response code="200">Instant playlist returned.</response>
|
/// <response code="200">Instant playlist returned.</response>
|
||||||
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
|
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
|
||||||
[HttpGet("MusicGenres/InstantMix")]
|
[HttpGet("MusicGenres/{id}/InstantMix")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenres(
|
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenres(
|
||||||
[FromRoute, Required] Guid id,
|
[FromRoute, Required] Guid id,
|
||||||
|
|
|
@ -60,7 +60,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets items based on a query.
|
/// Gets items based on a query.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uId">The user id supplied in the /Users/{uid}/Items.</param>
|
|
||||||
/// <param name="userId">The user id supplied as query parameter.</param>
|
/// <param name="userId">The user id supplied as query parameter.</param>
|
||||||
/// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</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="hasThemeSong">Optional filter by items with theme songs.</param>
|
||||||
|
@ -143,10 +142,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="enableImages">Optional, include image information in output.</param>
|
/// <param name="enableImages">Optional, include image information in output.</param>
|
||||||
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
|
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
|
||||||
[HttpGet("Items")]
|
[HttpGet("Items")]
|
||||||
[HttpGet("Users/{uId}/Items", Name = "GetItems_2")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public ActionResult<QueryResult<BaseItemDto>> GetItems(
|
public ActionResult<QueryResult<BaseItemDto>> GetItems(
|
||||||
[FromRoute] Guid? uId,
|
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery] Guid? userId,
|
||||||
[FromQuery] string? maxOfficialRating,
|
[FromQuery] string? maxOfficialRating,
|
||||||
[FromQuery] bool? hasThemeSong,
|
[FromQuery] bool? hasThemeSong,
|
||||||
|
@ -228,9 +225,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] bool enableTotalRecordCount = true,
|
[FromQuery] bool enableTotalRecordCount = true,
|
||||||
[FromQuery] bool? enableImages = true)
|
[FromQuery] bool? enableImages = true)
|
||||||
{
|
{
|
||||||
// use user id route parameter over query parameter
|
|
||||||
userId = uId ?? userId;
|
|
||||||
|
|
||||||
var user = userId.HasValue && !userId.Equals(Guid.Empty)
|
var user = userId.HasValue && !userId.Equals(Guid.Empty)
|
||||||
? _userManager.GetUserById(userId.Value)
|
? _userManager.GetUserById(userId.Value)
|
||||||
: null;
|
: null;
|
||||||
|
@ -505,6 +499,257 @@ namespace Jellyfin.Api.Controllers
|
||||||
return new QueryResult<BaseItemDto> { StartIndex = startIndex.GetValueOrDefault(), TotalRecordCount = result.TotalRecordCount, Items = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user) };
|
return new QueryResult<BaseItemDto> { StartIndex = startIndex.GetValueOrDefault(), TotalRecordCount = result.TotalRecordCount, Items = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets items based on a query.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">The user id supplied as query parameter.</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>
|
||||||
|
/// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
|
||||||
|
/// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
|
||||||
|
/// <param name="hasTrailer">Optional filter by items with trailers.</param>
|
||||||
|
/// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
|
||||||
|
/// <param name="parentIndexNumber">Optional filter by parent index number.</param>
|
||||||
|
/// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
|
||||||
|
/// <param name="isHd">Optional filter by items that are HD or not.</param>
|
||||||
|
/// <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="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>
|
||||||
|
/// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
|
||||||
|
/// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
|
||||||
|
/// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
|
||||||
|
/// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.</param>
|
||||||
|
/// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
|
||||||
|
/// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
|
||||||
|
/// <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="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="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="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="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="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="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="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>
|
||||||
|
/// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
|
||||||
|
/// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
|
||||||
|
/// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
|
||||||
|
/// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
|
||||||
|
/// <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="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="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(
|
||||||
|
[FromRoute] Guid userId,
|
||||||
|
[FromQuery] string? maxOfficialRating,
|
||||||
|
[FromQuery] bool? hasThemeSong,
|
||||||
|
[FromQuery] bool? hasThemeVideo,
|
||||||
|
[FromQuery] bool? hasSubtitles,
|
||||||
|
[FromQuery] bool? hasSpecialFeature,
|
||||||
|
[FromQuery] bool? hasTrailer,
|
||||||
|
[FromQuery] string? adjacentTo,
|
||||||
|
[FromQuery] int? parentIndexNumber,
|
||||||
|
[FromQuery] bool? hasParentalRating,
|
||||||
|
[FromQuery] bool? isHd,
|
||||||
|
[FromQuery] bool? is4K,
|
||||||
|
[FromQuery] string? locationTypes,
|
||||||
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes,
|
||||||
|
[FromQuery] bool? isMissing,
|
||||||
|
[FromQuery] bool? isUnaired,
|
||||||
|
[FromQuery] double? minCommunityRating,
|
||||||
|
[FromQuery] double? minCriticRating,
|
||||||
|
[FromQuery] DateTime? minPremiereDate,
|
||||||
|
[FromQuery] DateTime? minDateLastSaved,
|
||||||
|
[FromQuery] DateTime? minDateLastSavedForUser,
|
||||||
|
[FromQuery] DateTime? maxPremiereDate,
|
||||||
|
[FromQuery] bool? hasOverview,
|
||||||
|
[FromQuery] bool? hasImdbId,
|
||||||
|
[FromQuery] bool? hasTmdbId,
|
||||||
|
[FromQuery] bool? hasTvdbId,
|
||||||
|
[FromQuery] string? excludeItemIds,
|
||||||
|
[FromQuery] int? startIndex,
|
||||||
|
[FromQuery] int? limit,
|
||||||
|
[FromQuery] bool? recursive,
|
||||||
|
[FromQuery] string? searchTerm,
|
||||||
|
[FromQuery] string? sortOrder,
|
||||||
|
[FromQuery] string? parentId,
|
||||||
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
|
[FromQuery] string? excludeItemTypes,
|
||||||
|
[FromQuery] string? includeItemTypes,
|
||||||
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
||||||
|
[FromQuery] bool? isFavorite,
|
||||||
|
[FromQuery] string? mediaTypes,
|
||||||
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
|
||||||
|
[FromQuery] string? sortBy,
|
||||||
|
[FromQuery] bool? isPlayed,
|
||||||
|
[FromQuery] string? genres,
|
||||||
|
[FromQuery] string? officialRatings,
|
||||||
|
[FromQuery] string? tags,
|
||||||
|
[FromQuery] string? years,
|
||||||
|
[FromQuery] bool? enableUserData,
|
||||||
|
[FromQuery] int? imageTypeLimit,
|
||||||
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
||||||
|
[FromQuery] string? person,
|
||||||
|
[FromQuery] string? personIds,
|
||||||
|
[FromQuery] string? personTypes,
|
||||||
|
[FromQuery] string? studios,
|
||||||
|
[FromQuery] string? artists,
|
||||||
|
[FromQuery] string? excludeArtistIds,
|
||||||
|
[FromQuery] string? artistIds,
|
||||||
|
[FromQuery] string? albumArtistIds,
|
||||||
|
[FromQuery] string? contributingArtistIds,
|
||||||
|
[FromQuery] string? albums,
|
||||||
|
[FromQuery] string? albumIds,
|
||||||
|
[FromQuery] string? ids,
|
||||||
|
[FromQuery] string? videoTypes,
|
||||||
|
[FromQuery] string? minOfficialRating,
|
||||||
|
[FromQuery] bool? isLocked,
|
||||||
|
[FromQuery] bool? isPlaceHolder,
|
||||||
|
[FromQuery] bool? hasOfficialRating,
|
||||||
|
[FromQuery] bool? collapseBoxSetItems,
|
||||||
|
[FromQuery] int? minWidth,
|
||||||
|
[FromQuery] int? minHeight,
|
||||||
|
[FromQuery] int? maxWidth,
|
||||||
|
[FromQuery] int? maxHeight,
|
||||||
|
[FromQuery] bool? is3D,
|
||||||
|
[FromQuery] string? seriesStatus,
|
||||||
|
[FromQuery] string? nameStartsWithOrGreater,
|
||||||
|
[FromQuery] string? nameStartsWith,
|
||||||
|
[FromQuery] string? nameLessThan,
|
||||||
|
[FromQuery] string? studioIds,
|
||||||
|
[FromQuery] string? genreIds,
|
||||||
|
[FromQuery] bool enableTotalRecordCount = true,
|
||||||
|
[FromQuery] bool? enableImages = true)
|
||||||
|
{
|
||||||
|
return GetItems(
|
||||||
|
userId,
|
||||||
|
maxOfficialRating,
|
||||||
|
hasThemeSong,
|
||||||
|
hasThemeVideo,
|
||||||
|
hasSubtitles,
|
||||||
|
hasSpecialFeature,
|
||||||
|
hasTrailer,
|
||||||
|
adjacentTo,
|
||||||
|
parentIndexNumber,
|
||||||
|
hasParentalRating,
|
||||||
|
isHd,
|
||||||
|
is4K,
|
||||||
|
locationTypes,
|
||||||
|
excludeLocationTypes,
|
||||||
|
isMissing,
|
||||||
|
isUnaired,
|
||||||
|
minCommunityRating,
|
||||||
|
minCriticRating,
|
||||||
|
minPremiereDate,
|
||||||
|
minDateLastSaved,
|
||||||
|
minDateLastSavedForUser,
|
||||||
|
maxPremiereDate,
|
||||||
|
hasOverview,
|
||||||
|
hasImdbId,
|
||||||
|
hasTmdbId,
|
||||||
|
hasTvdbId,
|
||||||
|
excludeItemIds,
|
||||||
|
startIndex,
|
||||||
|
limit,
|
||||||
|
recursive,
|
||||||
|
searchTerm,
|
||||||
|
sortOrder,
|
||||||
|
parentId,
|
||||||
|
fields,
|
||||||
|
excludeItemTypes,
|
||||||
|
includeItemTypes,
|
||||||
|
filters,
|
||||||
|
isFavorite,
|
||||||
|
mediaTypes,
|
||||||
|
imageTypes,
|
||||||
|
sortBy,
|
||||||
|
isPlayed,
|
||||||
|
genres,
|
||||||
|
officialRatings,
|
||||||
|
tags,
|
||||||
|
years,
|
||||||
|
enableUserData,
|
||||||
|
imageTypeLimit,
|
||||||
|
enableImageTypes,
|
||||||
|
person,
|
||||||
|
personIds,
|
||||||
|
personTypes,
|
||||||
|
studios,
|
||||||
|
artists,
|
||||||
|
excludeArtistIds,
|
||||||
|
artistIds,
|
||||||
|
albumArtistIds,
|
||||||
|
contributingArtistIds,
|
||||||
|
albums,
|
||||||
|
albumIds,
|
||||||
|
ids,
|
||||||
|
videoTypes,
|
||||||
|
minOfficialRating,
|
||||||
|
isLocked,
|
||||||
|
isPlaceHolder,
|
||||||
|
hasOfficialRating,
|
||||||
|
collapseBoxSetItems,
|
||||||
|
minWidth,
|
||||||
|
minHeight,
|
||||||
|
maxWidth,
|
||||||
|
maxHeight,
|
||||||
|
is3D,
|
||||||
|
seriesStatus,
|
||||||
|
nameStartsWithOrGreater,
|
||||||
|
nameStartsWith,
|
||||||
|
nameLessThan,
|
||||||
|
studioIds,
|
||||||
|
genreIds,
|
||||||
|
enableTotalRecordCount,
|
||||||
|
enableImages);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets items based on a query.
|
/// Gets items based on a query.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -193,7 +193,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="200">File returned.</response>
|
/// <response code="200">File returned.</response>
|
||||||
/// <returns>A <see cref="FileContentResult"/> with the subtitle file.</returns>
|
/// <returns>A <see cref="FileContentResult"/> with the subtitle file.</returns>
|
||||||
[HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")]
|
[HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")]
|
||||||
[HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks?}/Stream.{format}", Name = "GetSubtitle_2")]
|
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesFile("text/*")]
|
[ProducesFile("text/*")]
|
||||||
public async Task<ActionResult> GetSubtitle(
|
public async Task<ActionResult> GetSubtitle(
|
||||||
|
@ -204,7 +203,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] long? endPositionTicks,
|
[FromQuery] long? endPositionTicks,
|
||||||
[FromQuery] bool copyTimestamps = false,
|
[FromQuery] bool copyTimestamps = false,
|
||||||
[FromQuery] bool addVttTimeMap = false,
|
[FromQuery] bool addVttTimeMap = false,
|
||||||
[FromRoute] long startPositionTicks = 0)
|
[FromQuery] long startPositionTicks = 0)
|
||||||
{
|
{
|
||||||
if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -249,6 +248,43 @@ namespace Jellyfin.Api.Controllers
|
||||||
MimeTypes.GetMimeType("file." + format));
|
MimeTypes.GetMimeType("file." + format));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets subtitles in a specified format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item id.</param>
|
||||||
|
/// <param name="mediaSourceId">The media source id.</param>
|
||||||
|
/// <param name="index">The subtitle stream index.</param>
|
||||||
|
/// <param name="startPositionTicks">Optional. The start position of the subtitle in ticks.</param>
|
||||||
|
/// <param name="format">The format of the returned subtitle.</param>
|
||||||
|
/// <param name="endPositionTicks">Optional. The end position of the subtitle in ticks.</param>
|
||||||
|
/// <param name="copyTimestamps">Optional. Whether to copy the timestamps.</param>
|
||||||
|
/// <param name="addVttTimeMap">Optional. Whether to add a VTT time map.</param>
|
||||||
|
/// <response code="200">File returned.</response>
|
||||||
|
/// <returns>A <see cref="FileContentResult"/> with the subtitle file.</returns>
|
||||||
|
[HttpGet("Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks}/Stream.{format}")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesFile("text/*")]
|
||||||
|
public Task<ActionResult> GetSubtitleWithTicks(
|
||||||
|
[FromRoute, Required] Guid itemId,
|
||||||
|
[FromRoute, Required] string mediaSourceId,
|
||||||
|
[FromRoute, Required] int index,
|
||||||
|
[FromRoute, Required] long startPositionTicks,
|
||||||
|
[FromRoute, Required] string format,
|
||||||
|
[FromQuery] long? endPositionTicks,
|
||||||
|
[FromQuery] bool copyTimestamps = false,
|
||||||
|
[FromQuery] bool addVttTimeMap = false)
|
||||||
|
{
|
||||||
|
return GetSubtitle(
|
||||||
|
itemId,
|
||||||
|
mediaSourceId,
|
||||||
|
index,
|
||||||
|
format,
|
||||||
|
endPositionTicks,
|
||||||
|
copyTimestamps,
|
||||||
|
addVttTimeMap,
|
||||||
|
startPositionTicks);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an HLS subtitle playlist.
|
/// Gets an HLS subtitle playlist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -335,6 +371,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="204">Subtitle uploaded.</response>
|
/// <response code="204">Subtitle uploaded.</response>
|
||||||
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
[HttpPost("Videos/{itemId}/Subtitles")]
|
[HttpPost("Videos/{itemId}/Subtitles")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
public async Task<ActionResult> UploadSubtitle(
|
public async Task<ActionResult> UploadSubtitle(
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromBody, Required] UploadSubtitleDto body)
|
[FromBody, Required] UploadSubtitleDto body)
|
||||||
|
@ -446,6 +483,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[HttpGet("FallbackFont/Fonts/{name}")]
|
[HttpGet("FallbackFont/Fonts/{name}")]
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesFile("font/*")]
|
||||||
public ActionResult GetFallbackFont([FromRoute, Required] string name)
|
public ActionResult GetFallbackFont([FromRoute, Required] string name)
|
||||||
{
|
{
|
||||||
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
||||||
|
|
|
@ -197,7 +197,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
return _itemsController
|
return _itemsController
|
||||||
.GetItems(
|
.GetItems(
|
||||||
userId,
|
|
||||||
userId,
|
userId,
|
||||||
maxOfficialRating,
|
maxOfficialRating,
|
||||||
hasThemeSong,
|
hasThemeSong,
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Api.Attributes;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
|
@ -43,7 +44,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <response code="404">Video or attachment not found.</response>
|
/// <response code="404">Video or attachment not found.</response>
|
||||||
/// <returns>An <see cref="FileStreamResult"/> containing the attachment stream on success, or a <see cref="NotFoundResult"/> if the attachment could not be found.</returns>
|
/// <returns>An <see cref="FileStreamResult"/> containing the attachment stream on success, or a <see cref="NotFoundResult"/> if the attachment could not be found.</returns>
|
||||||
[HttpGet("{videoId}/{mediaSourceId}/Attachments/{index}")]
|
[HttpGet("{videoId}/{mediaSourceId}/Attachments/{index}")]
|
||||||
[Produces(MediaTypeNames.Application.Octet)]
|
[ProducesFile(MediaTypeNames.Application.Octet)]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult> GetAttachment(
|
public async Task<ActionResult> GetAttachment(
|
||||||
|
|
|
@ -326,15 +326,13 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="streamOptions">Optional. The streaming options.</param>
|
/// <param name="streamOptions">Optional. The streaming options.</param>
|
||||||
/// <response code="200">Video stream returned.</response>
|
/// <response code="200">Video stream returned.</response>
|
||||||
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
||||||
[HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetVideoStreamWithExt")]
|
|
||||||
[HttpGet("{itemId}/stream")]
|
[HttpGet("{itemId}/stream")]
|
||||||
[HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadVideoStreamWithExt")]
|
|
||||||
[HttpHead("{itemId}/stream", Name = "HeadVideoStream")]
|
[HttpHead("{itemId}/stream", Name = "HeadVideoStream")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesVideoFile]
|
[ProducesVideoFile]
|
||||||
public async Task<ActionResult> GetVideoStream(
|
public async Task<ActionResult> GetVideoStream(
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromRoute] string? container,
|
[FromQuery] string? container,
|
||||||
[FromQuery] bool? @static,
|
[FromQuery] bool? @static,
|
||||||
[FromQuery] string? @params,
|
[FromQuery] string? @params,
|
||||||
[FromQuery] string? tag,
|
[FromQuery] string? tag,
|
||||||
|
@ -529,5 +527,166 @@ namespace Jellyfin.Api.Controllers
|
||||||
_transcodingJobType,
|
_transcodingJobType,
|
||||||
cancellationTokenSource).ConfigureAwait(false);
|
cancellationTokenSource).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a video stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId">The item id.</param>
|
||||||
|
/// <param name="container">The video container. Possible values are: ts, webm, asf, wmv, ogv, mp4, m4v, mkv, mpeg, mpg, avi, 3gp, wmv, wtv, m2ts, mov, iso, flv. </param>
|
||||||
|
/// <param name="static">Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false.</param>
|
||||||
|
/// <param name="params">The streaming parameters.</param>
|
||||||
|
/// <param name="tag">The tag.</param>
|
||||||
|
/// <param name="deviceProfileId">Optional. The dlna device profile id to utilize.</param>
|
||||||
|
/// <param name="playSessionId">The play session id.</param>
|
||||||
|
/// <param name="segmentContainer">The segment container.</param>
|
||||||
|
/// <param name="segmentLength">The segment lenght.</param>
|
||||||
|
/// <param name="minSegments">The minimum number of segments.</param>
|
||||||
|
/// <param name="mediaSourceId">The media version id, if playing an alternate version.</param>
|
||||||
|
/// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
|
||||||
|
/// <param name="audioCodec">Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.</param>
|
||||||
|
/// <param name="enableAutoStreamCopy">Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.</param>
|
||||||
|
/// <param name="allowVideoStreamCopy">Whether or not to allow copying of the video stream url.</param>
|
||||||
|
/// <param name="allowAudioStreamCopy">Whether or not to allow copying of the audio stream url.</param>
|
||||||
|
/// <param name="breakOnNonKeyFrames">Optional. Whether to break on non key frames.</param>
|
||||||
|
/// <param name="audioSampleRate">Optional. Specify a specific audio sample rate, e.g. 44100.</param>
|
||||||
|
/// <param name="maxAudioBitDepth">Optional. The maximum audio bit depth.</param>
|
||||||
|
/// <param name="audioBitRate">Optional. Specify an audio bitrate to encode to, e.g. 128000. If omitted this will be left to encoder defaults.</param>
|
||||||
|
/// <param name="audioChannels">Optional. Specify a specific number of audio channels to encode to, e.g. 2.</param>
|
||||||
|
/// <param name="maxAudioChannels">Optional. Specify a maximum number of audio channels to encode to, e.g. 2.</param>
|
||||||
|
/// <param name="profile">Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.</param>
|
||||||
|
/// <param name="level">Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.</param>
|
||||||
|
/// <param name="framerate">Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
|
||||||
|
/// <param name="maxFramerate">Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.</param>
|
||||||
|
/// <param name="copyTimestamps">Whether or not to copy timestamps when transcoding with an offset. Defaults to false.</param>
|
||||||
|
/// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
|
||||||
|
/// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
|
||||||
|
/// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
|
||||||
|
/// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
|
||||||
|
/// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
|
||||||
|
/// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
|
||||||
|
/// <param name="maxRefFrames">Optional.</param>
|
||||||
|
/// <param name="maxVideoBitDepth">Optional. The maximum video bit depth.</param>
|
||||||
|
/// <param name="requireAvc">Optional. Whether to require avc.</param>
|
||||||
|
/// <param name="deInterlace">Optional. Whether to deinterlace the video.</param>
|
||||||
|
/// <param name="requireNonAnamorphic">Optional. Whether to require a non anamporphic stream.</param>
|
||||||
|
/// <param name="transcodingMaxAudioChannels">Optional. The maximum number of audio channels to transcode.</param>
|
||||||
|
/// <param name="cpuCoreLimit">Optional. The limit of how many cpu cores to use.</param>
|
||||||
|
/// <param name="liveStreamId">The live stream id.</param>
|
||||||
|
/// <param name="enableMpegtsM2TsMode">Optional. Whether to enable the MpegtsM2Ts mode.</param>
|
||||||
|
/// <param name="videoCodec">Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.</param>
|
||||||
|
/// <param name="subtitleCodec">Optional. Specify a subtitle codec to encode to.</param>
|
||||||
|
/// <param name="transcodingReasons">Optional. The transcoding reason.</param>
|
||||||
|
/// <param name="audioStreamIndex">Optional. The index of the audio stream to use. If omitted the first audio stream will be used.</param>
|
||||||
|
/// <param name="videoStreamIndex">Optional. The index of the video stream to use. If omitted the first video stream will be used.</param>
|
||||||
|
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
|
||||||
|
/// <param name="streamOptions">Optional. The streaming options.</param>
|
||||||
|
/// <response code="200">Video stream returned.</response>
|
||||||
|
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
|
||||||
|
[HttpGet("{itemId}/{stream=stream}.{container}")]
|
||||||
|
[HttpHead("{itemId}/{stream=stream}.{container}", Name = "HeadVideoStreamByContainer")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesVideoFile]
|
||||||
|
public Task<ActionResult> GetVideoStreamByContainer(
|
||||||
|
[FromRoute, Required] Guid itemId,
|
||||||
|
[FromRoute, Required] string container,
|
||||||
|
[FromQuery] bool? @static,
|
||||||
|
[FromQuery] string? @params,
|
||||||
|
[FromQuery] string? tag,
|
||||||
|
[FromQuery] string? deviceProfileId,
|
||||||
|
[FromQuery] string? playSessionId,
|
||||||
|
[FromQuery] string? segmentContainer,
|
||||||
|
[FromQuery] int? segmentLength,
|
||||||
|
[FromQuery] int? minSegments,
|
||||||
|
[FromQuery] string? mediaSourceId,
|
||||||
|
[FromQuery] string? deviceId,
|
||||||
|
[FromQuery] string? audioCodec,
|
||||||
|
[FromQuery] bool? enableAutoStreamCopy,
|
||||||
|
[FromQuery] bool? allowVideoStreamCopy,
|
||||||
|
[FromQuery] bool? allowAudioStreamCopy,
|
||||||
|
[FromQuery] bool? breakOnNonKeyFrames,
|
||||||
|
[FromQuery] int? audioSampleRate,
|
||||||
|
[FromQuery] int? maxAudioBitDepth,
|
||||||
|
[FromQuery] int? audioBitRate,
|
||||||
|
[FromQuery] int? audioChannels,
|
||||||
|
[FromQuery] int? maxAudioChannels,
|
||||||
|
[FromQuery] string? profile,
|
||||||
|
[FromQuery] string? level,
|
||||||
|
[FromQuery] float? framerate,
|
||||||
|
[FromQuery] float? maxFramerate,
|
||||||
|
[FromQuery] bool? copyTimestamps,
|
||||||
|
[FromQuery] long? startTimeTicks,
|
||||||
|
[FromQuery] int? width,
|
||||||
|
[FromQuery] int? height,
|
||||||
|
[FromQuery] int? videoBitRate,
|
||||||
|
[FromQuery] int? subtitleStreamIndex,
|
||||||
|
[FromQuery] SubtitleDeliveryMethod subtitleMethod,
|
||||||
|
[FromQuery] int? maxRefFrames,
|
||||||
|
[FromQuery] int? maxVideoBitDepth,
|
||||||
|
[FromQuery] bool? requireAvc,
|
||||||
|
[FromQuery] bool? deInterlace,
|
||||||
|
[FromQuery] bool? requireNonAnamorphic,
|
||||||
|
[FromQuery] int? transcodingMaxAudioChannels,
|
||||||
|
[FromQuery] int? cpuCoreLimit,
|
||||||
|
[FromQuery] string? liveStreamId,
|
||||||
|
[FromQuery] bool? enableMpegtsM2TsMode,
|
||||||
|
[FromQuery] string? videoCodec,
|
||||||
|
[FromQuery] string? subtitleCodec,
|
||||||
|
[FromQuery] string? transcodingReasons,
|
||||||
|
[FromQuery] int? audioStreamIndex,
|
||||||
|
[FromQuery] int? videoStreamIndex,
|
||||||
|
[FromQuery] EncodingContext context,
|
||||||
|
[FromQuery] Dictionary<string, string> streamOptions)
|
||||||
|
{
|
||||||
|
return GetVideoStream(
|
||||||
|
itemId,
|
||||||
|
container,
|
||||||
|
@static,
|
||||||
|
@params,
|
||||||
|
tag,
|
||||||
|
deviceProfileId,
|
||||||
|
playSessionId,
|
||||||
|
segmentContainer,
|
||||||
|
segmentLength,
|
||||||
|
minSegments,
|
||||||
|
mediaSourceId,
|
||||||
|
deviceId,
|
||||||
|
audioCodec,
|
||||||
|
enableAutoStreamCopy,
|
||||||
|
allowVideoStreamCopy,
|
||||||
|
allowAudioStreamCopy,
|
||||||
|
breakOnNonKeyFrames,
|
||||||
|
audioSampleRate,
|
||||||
|
maxAudioBitDepth,
|
||||||
|
audioBitRate,
|
||||||
|
audioChannels,
|
||||||
|
maxAudioChannels,
|
||||||
|
profile,
|
||||||
|
level,
|
||||||
|
framerate,
|
||||||
|
maxFramerate,
|
||||||
|
copyTimestamps,
|
||||||
|
startTimeTicks,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
videoBitRate,
|
||||||
|
subtitleStreamIndex,
|
||||||
|
subtitleMethod,
|
||||||
|
maxRefFrames,
|
||||||
|
maxVideoBitDepth,
|
||||||
|
requireAvc,
|
||||||
|
deInterlace,
|
||||||
|
requireNonAnamorphic,
|
||||||
|
transcodingMaxAudioChannels,
|
||||||
|
cpuCoreLimit,
|
||||||
|
liveStreamId,
|
||||||
|
enableMpegtsM2TsMode,
|
||||||
|
videoCodec,
|
||||||
|
subtitleCodec,
|
||||||
|
transcodingReasons,
|
||||||
|
audioStreamIndex,
|
||||||
|
videoStreamIndex,
|
||||||
|
context,
|
||||||
|
streamOptions);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1647,7 +1647,7 @@ namespace MediaBrowser.Model.Dlna
|
||||||
|
|
||||||
// strip spaces to avoid having to encode
|
// strip spaces to avoid having to encode
|
||||||
var values = value
|
var values = value
|
||||||
.Split('|', StringSplitOptions.RemoveEmptyEntries);
|
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
if (condition.Condition == ProfileConditionType.Equals || condition.Condition == ProfileConditionType.EqualsAny)
|
if (condition.Condition == ProfileConditionType.Equals || condition.Condition == ProfileConditionType.EqualsAny)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user