aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Api')
-rw-r--r--Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs53
-rw-r--r--Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs14
-rw-r--r--Jellyfin.Api/Constants/Policies.cs18
-rw-r--r--Jellyfin.Api/Controllers/DisplayPreferencesController.cs22
-rw-r--r--Jellyfin.Api/Controllers/ImageController.cs8
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs11
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs3
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs28
-rw-r--r--Jellyfin.Api/Controllers/SyncPlayController.cs25
-rw-r--r--Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs28
-rw-r--r--Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs2
11 files changed, 158 insertions, 54 deletions
diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
index b5932ea6b4..b898ac76c8 100644
--- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
+++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
@@ -3,6 +3,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.SyncPlay;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -13,20 +14,24 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
/// </summary>
public class SyncPlayAccessHandler : BaseAuthorizationHandler<SyncPlayAccessRequirement>
{
+ private readonly ISyncPlayManager _syncPlayManager;
private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="SyncPlayAccessHandler"/> class.
/// </summary>
+ /// <param name="syncPlayManager">Instance of the <see cref="ISyncPlayManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
/// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
public SyncPlayAccessHandler(
+ ISyncPlayManager syncPlayManager,
IUserManager userManager,
INetworkManager networkManager,
IHttpContextAccessor httpContextAccessor)
: base(userManager, networkManager, httpContextAccessor)
{
+ _syncPlayManager = syncPlayManager;
_userManager = userManager;
}
@@ -42,10 +47,52 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
var userId = ClaimHelpers.GetUserId(context.User);
var user = _userManager.GetUserById(userId!.Value);
- if ((requirement.RequiredAccess.HasValue && user.SyncPlayAccess == requirement.RequiredAccess)
- || user.SyncPlayAccess == SyncPlayAccess.CreateAndJoinGroups)
+ if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess)
{
- context.Succeed(requirement);
+ if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups
+ || user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups
+ || _syncPlayManager.IsUserActive(userId!.Value))
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
+ }
+ else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.CreateGroup)
+ {
+ if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups)
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
+ }
+ else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.JoinGroup)
+ {
+ if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups
+ || user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups)
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
+ }
+ else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.IsInGroup)
+ {
+ if (_syncPlayManager.IsUserActive(userId!.Value))
+ {
+ context.Succeed(requirement);
+ }
+ else
+ {
+ context.Fail();
+ }
}
else
{
diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
index 7fcaf69f6e..6fab4c0ad8 100644
--- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
+++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessRequirement.cs
@@ -11,23 +11,15 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
/// <summary>
/// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
/// </summary>
- /// <param name="requiredAccess">A value of <see cref="SyncPlayAccess"/>.</param>
- public SyncPlayAccessRequirement(SyncPlayAccess requiredAccess)
+ /// <param name="requiredAccess">A value of <see cref="SyncPlayAccessRequirementType"/>.</param>
+ public SyncPlayAccessRequirement(SyncPlayAccessRequirementType requiredAccess)
{
RequiredAccess = requiredAccess;
}
/// <summary>
- /// Initializes a new instance of the <see cref="SyncPlayAccessRequirement"/> class.
- /// </summary>
- public SyncPlayAccessRequirement()
- {
- RequiredAccess = null;
- }
-
- /// <summary>
/// Gets the required SyncPlay access.
/// </summary>
- public SyncPlayAccess? RequiredAccess { get; }
+ public SyncPlayAccessRequirementType RequiredAccess { get; }
}
}
diff --git a/Jellyfin.Api/Constants/Policies.cs b/Jellyfin.Api/Constants/Policies.cs
index b35ceea1a3..632dedb3cd 100644
--- a/Jellyfin.Api/Constants/Policies.cs
+++ b/Jellyfin.Api/Constants/Policies.cs
@@ -51,13 +51,23 @@ namespace Jellyfin.Api.Constants
public const string FirstTimeSetupOrIgnoreParentalControl = "FirstTimeSetupOrIgnoreParentalControl";
/// <summary>
- /// Policy name for requiring access to SyncPlay.
+ /// Policy name for accessing SyncPlay.
/// </summary>
- public const string SyncPlayAccess = "SyncPlayAccess";
+ public const string SyncPlayHasAccess = "SyncPlayHasAccess";
/// <summary>
- /// Policy name for requiring group creation access to SyncPlay.
+ /// Policy name for creating a SyncPlay group.
/// </summary>
- public const string SyncPlayCreateGroupAccess = "SyncPlayCreateGroupAccess";
+ public const string SyncPlayCreateGroup = "SyncPlayCreateGroup";
+
+ /// <summary>
+ /// Policy name for joining a SyncPlay group.
+ /// </summary>
+ public const string SyncPlayJoinGroup = "SyncPlayJoinGroup";
+
+ /// <summary>
+ /// Policy name for accessing a SyncPlay group.
+ /// </summary>
+ public const string SyncPlayIsInGroup = "SyncPlayIsInGroup";
}
}
diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
index 8b8f63015e..f7bb968f0b 100644
--- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
+++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Model.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Controllers
{
@@ -22,14 +23,17 @@ namespace Jellyfin.Api.Controllers
public class DisplayPreferencesController : BaseJellyfinApiController
{
private readonly IDisplayPreferencesManager _displayPreferencesManager;
+ private readonly ILogger<DisplayPreferencesController> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferencesController"/> class.
/// </summary>
/// <param name="displayPreferencesManager">Instance of <see cref="IDisplayPreferencesManager"/> interface.</param>
- public DisplayPreferencesController(IDisplayPreferencesManager displayPreferencesManager)
+ /// <param name="logger">Instance of <see cref="ILogger{DisplayPreferencesController}"/> interface.</param>
+ public DisplayPreferencesController(IDisplayPreferencesManager displayPreferencesManager, ILogger<DisplayPreferencesController> logger)
{
_displayPreferencesManager = displayPreferencesManager;
+ _logger = logger;
}
/// <summary>
@@ -61,7 +65,6 @@ namespace Jellyfin.Api.Controllers
{
Client = displayPreferences.Client,
Id = displayPreferences.ItemId.ToString(),
- ViewType = itemPreferences.ViewType.ToString(),
SortBy = itemPreferences.SortBy,
SortOrder = itemPreferences.SortOrder,
IndexBy = displayPreferences.IndexBy?.ToString(),
@@ -77,11 +80,6 @@ namespace Jellyfin.Api.Controllers
dto.CustomPrefs["homesection" + homeSection.Order] = homeSection.Type.ToString().ToLowerInvariant();
}
- foreach (var itemDisplayPreferences in _displayPreferencesManager.ListItemDisplayPreferences(displayPreferences.UserId, displayPreferences.Client))
- {
- dto.CustomPrefs["landing-" + itemDisplayPreferences.ItemId] = itemDisplayPreferences.ViewType.ToString().ToLowerInvariant();
- }
-
dto.CustomPrefs["chromecastVersion"] = displayPreferences.ChromecastVersion.ToString().ToLowerInvariant();
dto.CustomPrefs["skipForwardLength"] = displayPreferences.SkipForwardLength.ToString(CultureInfo.InvariantCulture);
dto.CustomPrefs["skipBackLength"] = displayPreferences.SkipBackwardLength.ToString(CultureInfo.InvariantCulture);
@@ -189,10 +187,9 @@ namespace Jellyfin.Api.Controllers
foreach (var key in displayPreferences.CustomPrefs.Keys.Where(key => key.StartsWith("landing-", StringComparison.OrdinalIgnoreCase)))
{
- if (Guid.TryParse(key.AsSpan().Slice("landing-".Length), out var preferenceId))
+ if (!Enum.TryParse<ViewType>(displayPreferences.CustomPrefs[key], true, out var type))
{
- var itemPreferences = _displayPreferencesManager.GetItemDisplayPreferences(existingDisplayPreferences.UserId, preferenceId, existingDisplayPreferences.Client);
- itemPreferences.ViewType = Enum.Parse<ViewType>(displayPreferences.ViewType);
+ _logger.LogError("Invalid ViewType: {LandingScreenOption}", displayPreferences.CustomPrefs[key]);
displayPreferences.CustomPrefs.Remove(key);
}
}
@@ -204,11 +201,6 @@ namespace Jellyfin.Api.Controllers
itemPrefs.RememberSorting = displayPreferences.RememberSorting;
itemPrefs.ItemId = itemId;
- if (Enum.TryParse<ViewType>(displayPreferences.ViewType, true, out var viewType))
- {
- itemPrefs.ViewType = viewType;
- }
-
// Set all remaining custom preferences.
_displayPreferencesManager.SetCustomItemDisplayPreferences(userId, itemId, existingDisplayPreferences.Client, displayPreferences.CustomPrefs);
_displayPreferencesManager.SaveChanges();
diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs
index e828a0801b..c606d327c5 100644
--- a/Jellyfin.Api/Controllers/ImageController.cs
+++ b/Jellyfin.Api/Controllers/ImageController.cs
@@ -325,9 +325,11 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
+ await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
+
// 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 _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return NoContent();
@@ -358,9 +360,11 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
+ await using var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
+
// 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 _providerManager.SaveImage(item, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return NoContent();
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 56d4b3933e..6f2d432277 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -1119,20 +1119,15 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Set channel mappings.
/// </summary>
- /// <param name="providerId">Provider id.</param>
- /// <param name="tunerChannelId">Tuner channel id.</param>
- /// <param name="providerChannelId">Provider channel id.</param>
+ /// <param name="setChannelMappingDto">The set channel mapping dto.</param>
/// <response code="200">Created channel mapping returned.</response>
/// <returns>An <see cref="OkResult"/> containing the created channel mapping.</returns>
[HttpPost("ChannelMappings")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<TunerChannelMapping>> SetChannelMapping(
- [FromQuery] string? providerId,
- [FromQuery] string? tunerChannelId,
- [FromQuery] string? providerChannelId)
+ public async Task<ActionResult<TunerChannelMapping>> SetChannelMapping([FromBody, Required] SetChannelMappingDto setChannelMappingDto)
{
- return await _liveTvManager.SetChannelMapping(providerId, tunerChannelId, providerChannelId).ConfigureAwait(false);
+ return await _liveTvManager.SetChannelMapping(setChannelMappingDto.ProviderId, setChannelMappingDto.TunerChannelId, setChannelMappingDto.ProviderChannelId).ConfigureAwait(false);
}
/// <summary>
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index a76dc057a0..2a1da31c9e 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -17,6 +17,7 @@ using MediaBrowser.Model.MediaInfo;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Controllers
@@ -119,7 +120,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableTranscoding,
[FromQuery] bool? allowVideoStreamCopy,
[FromQuery] bool? allowAudioStreamCopy,
- [FromBody] PlaybackInfoDto? playbackInfoDto)
+ [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto)
{
var authInfo = _authContext.GetAuthorizationInfo(Request);
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index 3e55434c06..fcdad4bc72 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
@@ -17,6 +18,7 @@ using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Jellyfin.Api.Controllers
{
@@ -53,6 +55,13 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Creates a new playlist.
/// </summary>
+ /// <remarks>
+ /// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
+ /// </remarks>
+ /// <param name="name">The playlist name.</param>
+ /// <param name="ids">The item ids.</param>
+ /// <param name="userId">The user id.</param>
+ /// <param name="mediaType">The media type.</param>
/// <param name="createPlaylistRequest">The create playlist payload.</param>
/// <returns>
/// A <see cref="Task" /> that represents the asynchronous operation to create a playlist.
@@ -61,14 +70,23 @@ namespace Jellyfin.Api.Controllers
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist(
- [FromBody, Required] CreatePlaylistDto createPlaylistRequest)
+ [FromQuery] string? name,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] IReadOnlyList<Guid> ids,
+ [FromQuery] Guid? userId,
+ [FromQuery] string? mediaType,
+ [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] CreatePlaylistDto? createPlaylistRequest)
{
+ if (ids.Count == 0)
+ {
+ ids = createPlaylistRequest?.Ids ?? Array.Empty<Guid>();
+ }
+
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
{
- Name = createPlaylistRequest.Name,
- ItemIdList = createPlaylistRequest.Ids,
- UserId = createPlaylistRequest.UserId,
- MediaType = createPlaylistRequest.MediaType
+ Name = name ?? createPlaylistRequest?.Name,
+ ItemIdList = ids,
+ UserId = userId ?? createPlaylistRequest?.UserId ?? default,
+ MediaType = mediaType ?? createPlaylistRequest?.MediaType
}).ConfigureAwait(false);
return result;
diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs
index 471c9180da..82cbe58df2 100644
--- a/Jellyfin.Api/Controllers/SyncPlayController.cs
+++ b/Jellyfin.Api/Controllers/SyncPlayController.cs
@@ -20,7 +20,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// The sync play controller.
/// </summary>
- [Authorize(Policy = Policies.SyncPlayAccess)]
+ [Authorize(Policy = Policies.SyncPlayHasAccess)]
public class SyncPlayController : BaseJellyfinApiController
{
private readonly ISessionManager _sessionManager;
@@ -51,7 +51,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("New")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- [Authorize(Policy = Policies.SyncPlayCreateGroupAccess)]
+ [Authorize(Policy = Policies.SyncPlayCreateGroup)]
public ActionResult SyncPlayCreateGroup(
[FromBody, Required] NewGroupRequestDto requestData)
{
@@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Join")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- [Authorize(Policy = Policies.SyncPlayAccess)]
+ [Authorize(Policy = Policies.SyncPlayJoinGroup)]
public ActionResult SyncPlayJoinGroup(
[FromBody, Required] JoinGroupRequestDto requestData)
{
@@ -86,6 +86,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Leave")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayLeaveGroup()
{
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
@@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="IEnumerable{GroupInfoView}"/> containing the available SyncPlay groups.</returns>
[HttpGet("List")]
[ProducesResponseType(StatusCodes.Status200OK)]
- [Authorize(Policy = Policies.SyncPlayAccess)]
+ [Authorize(Policy = Policies.SyncPlayJoinGroup)]
public ActionResult<IEnumerable<GroupInfoDto>> SyncPlayGetGroups()
{
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
@@ -117,6 +118,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("SetNewQueue")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlaySetNewQueue(
[FromBody, Required] PlayRequestDto requestData)
{
@@ -137,6 +139,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("SetPlaylistItem")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlaySetPlaylistItem(
[FromBody, Required] SetPlaylistItemRequestDto requestData)
{
@@ -154,6 +157,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("RemoveFromPlaylist")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayRemoveFromPlaylist(
[FromBody, Required] RemoveFromPlaylistRequestDto requestData)
{
@@ -171,6 +175,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("MovePlaylistItem")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayMovePlaylistItem(
[FromBody, Required] MovePlaylistItemRequestDto requestData)
{
@@ -188,6 +193,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Queue")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayQueue(
[FromBody, Required] QueueRequestDto requestData)
{
@@ -204,6 +210,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Unpause")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayUnpause()
{
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
@@ -219,6 +226,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Pause")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayPause()
{
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
@@ -234,6 +242,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Stop")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayStop()
{
var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request);
@@ -250,6 +259,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Seek")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlaySeek(
[FromBody, Required] SeekRequestDto requestData)
{
@@ -267,6 +277,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Buffering")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayBuffering(
[FromBody, Required] BufferRequestDto requestData)
{
@@ -288,6 +299,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Ready")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayReady(
[FromBody, Required] ReadyRequestDto requestData)
{
@@ -309,6 +321,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("SetIgnoreWait")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlaySetIgnoreWait(
[FromBody, Required] IgnoreWaitRequestDto requestData)
{
@@ -326,6 +339,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("NextItem")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayNextItem(
[FromBody, Required] NextItemRequestDto requestData)
{
@@ -343,6 +357,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("PreviousItem")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlayPreviousItem(
[FromBody, Required] PreviousItemRequestDto requestData)
{
@@ -360,6 +375,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("SetRepeatMode")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlaySetRepeatMode(
[FromBody, Required] SetRepeatModeRequestDto requestData)
{
@@ -377,6 +393,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("SetShuffleMode")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
+ [Authorize(Policy = Policies.SyncPlayIsInGroup)]
public ActionResult SyncPlaySetShuffleMode(
[FromBody, Required] SetShuffleModeRequestDto requestData)
{
diff --git a/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs b/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs
new file mode 100644
index 0000000000..2ddaa89e8b
--- /dev/null
+++ b/Jellyfin.Api/Models/LiveTvDtos/SetChannelMappingDto.cs
@@ -0,0 +1,28 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Jellyfin.Api.Models.LiveTvDtos
+{
+ /// <summary>
+ /// Set channel mapping dto.
+ /// </summary>
+ public class SetChannelMappingDto
+ {
+ /// <summary>
+ /// Gets or sets the provider id.
+ /// </summary>
+ [Required]
+ public string ProviderId { get; set; } = string.Empty;
+
+ /// <summary>
+ /// Gets or sets the tuner channel id.
+ /// </summary>
+ [Required]
+ public string TunerChannelId { get; set; } = string.Empty;
+
+ /// <summary>
+ /// Gets or sets the provider channel id.
+ /// </summary>
+ [Required]
+ public string ProviderChannelId { get; set; } = string.Empty;
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
index d0d6889fc8..65d4b644e4 100644
--- a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
+++ b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
@@ -24,7 +24,7 @@ namespace Jellyfin.Api.Models.PlaylistDtos
/// <summary>
/// Gets or sets the user id.
/// </summary>
- public Guid UserId { get; set; }
+ public Guid? UserId { get; set; }
/// <summary>
/// Gets or sets the media type.