From 9a2bcd6266fb222491abe6ea31d5e7e734699d5f Mon Sep 17 00:00:00 2001 From: David Date: Wed, 15 Jul 2020 16:15:17 +0200 Subject: [PATCH 1/5] Move SyncPlay api to Jellyfin.Api --- .../Controllers/SyncPlayController.cs | 186 +++++++++++ .../Controllers/TimeSyncController.cs | 39 +++ MediaBrowser.Api/SyncPlay/SyncPlayService.cs | 302 ------------------ MediaBrowser.Api/SyncPlay/TimeSyncService.cs | 52 --- 4 files changed, 225 insertions(+), 354 deletions(-) create mode 100644 Jellyfin.Api/Controllers/SyncPlayController.cs create mode 100644 Jellyfin.Api/Controllers/TimeSyncController.cs delete mode 100644 MediaBrowser.Api/SyncPlay/SyncPlayService.cs delete mode 100644 MediaBrowser.Api/SyncPlay/TimeSyncService.cs diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs new file mode 100644 index 0000000000..99f828518f --- /dev/null +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Threading; +using Jellyfin.Api.Constants; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Session; +using MediaBrowser.Controller.SyncPlay; +using MediaBrowser.Model.SyncPlay; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers +{ + /// + /// The sync play controller. + /// + [Authorize(Policy = Policies.DefaultAuthorization)] + public class SyncPlayController : BaseJellyfinApiController + { + private readonly ISessionManager _sessionManager; + private readonly IAuthorizationContext _authorizationContext; + private readonly ISyncPlayManager _syncPlayManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public SyncPlayController( + ISessionManager sessionManager, + IAuthorizationContext authorizationContext, + ISyncPlayManager syncPlayManager) + { + _sessionManager = sessionManager; + _authorizationContext = authorizationContext; + _syncPlayManager = syncPlayManager; + } + + /// + /// Create a new SyncPlay group. + /// + /// A indicating success. + [HttpPost("New")] + public ActionResult CreateNewGroup() + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + _syncPlayManager.NewGroup(currentSession, CancellationToken.None); + return NoContent(); + } + + /// + /// Join an existing SyncPlay group. + /// + /// The sync play group id. + /// A indicating success. + [HttpPost("Join")] + public ActionResult JoinGroup([FromQuery, Required] Guid groupId) + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + + var joinRequest = new JoinGroupRequest() + { + GroupId = groupId + }; + + _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None); + return NoContent(); + } + + /// + /// Leave the joined SyncPlay group. + /// + /// A indicating success. + [HttpPost("Leave")] + public ActionResult LeaveGroup() + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None); + return NoContent(); + } + + /// + /// Gets all SyncPlay groups. + /// + /// Optional. Filter by item id. + /// An containing the available SyncPlay groups. + [HttpGet("List")] + public ActionResult> GetSyncPlayGroups([FromQuery] Guid? filterItemId) + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + return Ok(_syncPlayManager.ListGroups(currentSession, filterItemId.HasValue ? filterItemId.Value : Guid.Empty)); + } + + /// + /// Request play in SyncPlay group. + /// + /// A indicating success. + [HttpPost] + public ActionResult Play() + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + var syncPlayRequest = new PlaybackRequest() + { + Type = PlaybackRequestType.Play + }; + _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); + return NoContent(); + } + + /// + /// Request pause in SyncPlay group. + /// + /// A indicating success. + [HttpPost] + public ActionResult Pause() + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + var syncPlayRequest = new PlaybackRequest() + { + Type = PlaybackRequestType.Pause + }; + _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); + return NoContent(); + } + + /// + /// Request seek in SyncPlay group. + /// + /// The playback position in ticks. + /// A indicating success. + [HttpPost] + public ActionResult Seek([FromQuery] long positionTicks) + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + var syncPlayRequest = new PlaybackRequest() + { + Type = PlaybackRequestType.Seek, + PositionTicks = positionTicks + }; + _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); + return NoContent(); + } + + /// + /// Request group wait in SyncPlay group while buffering. + /// + /// When the request has been made by the client. + /// The playback position in ticks. + /// Whether the buffering is done. + /// A indicating success. + [HttpPost] + public ActionResult Buffering([FromQuery] DateTime when, [FromQuery] long positionTicks, [FromQuery] bool bufferingDone) + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + var syncPlayRequest = new PlaybackRequest() + { + Type = bufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering, + When = when, + PositionTicks = positionTicks + }; + _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); + return NoContent(); + } + + /// + /// Update session ping. + /// + /// The ping. + /// A indicating success. + [HttpPost] + public ActionResult Ping([FromQuery] double ping) + { + var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); + var syncPlayRequest = new PlaybackRequest() + { + Type = PlaybackRequestType.UpdatePing, + Ping = Convert.ToInt64(ping) + }; + _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); + return NoContent(); + } + } +} diff --git a/Jellyfin.Api/Controllers/TimeSyncController.cs b/Jellyfin.Api/Controllers/TimeSyncController.cs new file mode 100644 index 0000000000..57a720b26c --- /dev/null +++ b/Jellyfin.Api/Controllers/TimeSyncController.cs @@ -0,0 +1,39 @@ +using System; +using System.Globalization; +using MediaBrowser.Model.SyncPlay; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers +{ + /// + /// The time sync controller. + /// + [Route("/GetUtcTime")] + public class TimeSyncController : BaseJellyfinApiController + { + /// + /// Gets the current utc time. + /// + /// Time returned. + /// An to sync the client and server time. + [HttpGet] + [ProducesResponseType(statusCode: StatusCodes.Status200OK)] + public ActionResult GetUtcTime() + { + // Important to keep the following line at the beginning + var requestReceptionTime = DateTime.UtcNow.ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo); + + var response = new UtcTimeResponse(); + response.RequestReceptionTime = requestReceptionTime; + + // Important to keep the following two lines at the end + var responseTransmissionTime = DateTime.UtcNow.ToUniversalTime().ToString("o", DateTimeFormatInfo.InvariantInfo); + response.ResponseTransmissionTime = responseTransmissionTime; + + // Implementing NTP on such a high level results in this useless + // information being sent. On the other hand it enables future additions. + return response; + } + } +} diff --git a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs deleted file mode 100644 index 1e14ea552c..0000000000 --- a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System.Threading; -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Session; -using MediaBrowser.Controller.SyncPlay; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.SyncPlay; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.SyncPlay -{ - [Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")] - [Authenticated] - public class SyncPlayNewGroup : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - } - - [Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")] - [Authenticated] - public class SyncPlayJoinGroup : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - - /// - /// Gets or sets the Group id. - /// - /// The Group id to join. - [ApiMember(Name = "GroupId", Description = "Group Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string GroupId { get; set; } - - /// - /// Gets or sets the playing item id. - /// - /// The client's currently playing item id. - [ApiMember(Name = "PlayingItemId", Description = "Client's playing item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlayingItemId { get; set; } - } - - [Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")] - [Authenticated] - public class SyncPlayLeaveGroup : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - } - - [Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")] - [Authenticated] - public class SyncPlayListGroups : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - - /// - /// Gets or sets the filter item id. - /// - /// The filter item id. - [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string FilterItemId { get; set; } - } - - [Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")] - [Authenticated] - public class SyncPlayPlayRequest : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - } - - [Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")] - [Authenticated] - public class SyncPlayPauseRequest : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - } - - [Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")] - [Authenticated] - public class SyncPlaySeekRequest : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - - [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")] - public long PositionTicks { get; set; } - } - - [Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")] - [Authenticated] - public class SyncPlayBufferingRequest : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - - /// - /// Gets or sets the date used to pin PositionTicks in time. - /// - /// The date related to PositionTicks. - [ApiMember(Name = "When", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string When { get; set; } - - [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")] - public long PositionTicks { get; set; } - - /// - /// Gets or sets whether this is a buffering or a buffering-done request. - /// - /// true if buffering is complete; false otherwise. - [ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")] - public bool BufferingDone { get; set; } - } - - [Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")] - [Authenticated] - public class SyncPlayUpdatePing : IReturnVoid - { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - - [ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")] - public double Ping { get; set; } - } - - /// - /// Class SyncPlayService. - /// - public class SyncPlayService : BaseApiService - { - /// - /// The session context. - /// - private readonly ISessionContext _sessionContext; - - /// - /// The SyncPlay manager. - /// - private readonly ISyncPlayManager _syncPlayManager; - - public SyncPlayService( - ILogger logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - ISessionContext sessionContext, - ISyncPlayManager syncPlayManager) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _sessionContext = sessionContext; - _syncPlayManager = syncPlayManager; - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayNewGroup request) - { - var currentSession = GetSession(_sessionContext); - _syncPlayManager.NewGroup(currentSession, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayJoinGroup request) - { - var currentSession = GetSession(_sessionContext); - - Guid groupId; - Guid playingItemId = Guid.Empty; - - if (!Guid.TryParse(request.GroupId, out groupId)) - { - Logger.LogError("JoinGroup: {0} is not a valid format for GroupId. Ignoring request.", request.GroupId); - return; - } - - // Both null and empty strings mean that client isn't playing anything - if (!string.IsNullOrEmpty(request.PlayingItemId) && !Guid.TryParse(request.PlayingItemId, out playingItemId)) - { - Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId); - return; - } - - var joinRequest = new JoinGroupRequest() - { - GroupId = groupId, - PlayingItemId = playingItemId - }; - - _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayLeaveGroup request) - { - var currentSession = GetSession(_sessionContext); - _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - /// The requested list of groups. - public List Post(SyncPlayListGroups request) - { - var currentSession = GetSession(_sessionContext); - var filterItemId = Guid.Empty; - - if (!string.IsNullOrEmpty(request.FilterItemId) && !Guid.TryParse(request.FilterItemId, out filterItemId)) - { - Logger.LogWarning("ListGroups: {0} is not a valid format for FilterItemId. Ignoring filter.", request.FilterItemId); - } - - return _syncPlayManager.ListGroups(currentSession, filterItemId); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayPlayRequest request) - { - var currentSession = GetSession(_sessionContext); - var syncPlayRequest = new PlaybackRequest() - { - Type = PlaybackRequestType.Play - }; - _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayPauseRequest request) - { - var currentSession = GetSession(_sessionContext); - var syncPlayRequest = new PlaybackRequest() - { - Type = PlaybackRequestType.Pause - }; - _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlaySeekRequest request) - { - var currentSession = GetSession(_sessionContext); - var syncPlayRequest = new PlaybackRequest() - { - Type = PlaybackRequestType.Seek, - PositionTicks = request.PositionTicks - }; - _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayBufferingRequest request) - { - var currentSession = GetSession(_sessionContext); - var syncPlayRequest = new PlaybackRequest() - { - Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering, - When = DateTime.Parse(request.When), - PositionTicks = request.PositionTicks - }; - _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); - } - - /// - /// Handles the specified request. - /// - /// The request. - public void Post(SyncPlayUpdatePing request) - { - var currentSession = GetSession(_sessionContext); - var syncPlayRequest = new PlaybackRequest() - { - Type = PlaybackRequestType.UpdatePing, - Ping = Convert.ToInt64(request.Ping) - }; - _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); - } - } -} diff --git a/MediaBrowser.Api/SyncPlay/TimeSyncService.cs b/MediaBrowser.Api/SyncPlay/TimeSyncService.cs deleted file mode 100644 index 4a9307e62f..0000000000 --- a/MediaBrowser.Api/SyncPlay/TimeSyncService.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.SyncPlay; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.SyncPlay -{ - [Route("/GetUtcTime", "GET", Summary = "Get UtcTime")] - public class GetUtcTime : IReturnVoid - { - // Nothing - } - - /// - /// Class TimeSyncService. - /// - public class TimeSyncService : BaseApiService - { - public TimeSyncService( - ILogger logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory) - : base(logger, serverConfigurationManager, httpResultFactory) - { - // Do nothing - } - - /// - /// Handles the specified request. - /// - /// The request. - /// The current UTC time response. - public UtcTimeResponse Get(GetUtcTime request) - { - // Important to keep the following line at the beginning - var requestReceptionTime = DateTime.UtcNow.ToUniversalTime().ToString("o"); - - var response = new UtcTimeResponse(); - response.RequestReceptionTime = requestReceptionTime; - - // Important to keep the following two lines at the end - var responseTransmissionTime = DateTime.UtcNow.ToUniversalTime().ToString("o"); - response.ResponseTransmissionTime = responseTransmissionTime; - - // Implementing NTP on such a high level results in this useless - // information being sent. On the other hand it enables future additions. - return response; - } - } -} From cbf5c682e93ce9e60a80b0130d04e4493f4cb684 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 21 Jul 2020 22:06:07 +0200 Subject: [PATCH 2/5] Change enum values --- Jellyfin.Api/Controllers/SyncPlayController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index 99f828518f..c0544091c5 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -157,7 +157,7 @@ namespace Jellyfin.Api.Controllers var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() { - Type = bufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering, + Type = bufferingDone ? PlaybackRequestType.Ready : PlaybackRequestType.Buffer, When = when, PositionTicks = positionTicks }; @@ -176,7 +176,7 @@ namespace Jellyfin.Api.Controllers var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() { - Type = PlaybackRequestType.UpdatePing, + Type = PlaybackRequestType.Ping, Ping = Convert.ToInt64(ping) }; _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); From 9996afbf25ee7025bd7d0d7bceb0dbd75253b6d7 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 22 Jul 2020 10:20:51 +0200 Subject: [PATCH 3/5] Add response code documentation --- .../Controllers/SyncPlayController.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index c0544091c5..3f40c7309b 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Model.SyncPlay; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Controllers @@ -42,8 +43,10 @@ namespace Jellyfin.Api.Controllers /// /// Create a new SyncPlay group. /// + /// New group created. /// A indicating success. [HttpPost("New")] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult CreateNewGroup() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -55,8 +58,10 @@ namespace Jellyfin.Api.Controllers /// Join an existing SyncPlay group. /// /// The sync play group id. + /// Group join successful. /// A indicating success. [HttpPost("Join")] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult JoinGroup([FromQuery, Required] Guid groupId) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -73,8 +78,10 @@ namespace Jellyfin.Api.Controllers /// /// Leave the joined SyncPlay group. /// + /// Group leave successful. /// A indicating success. [HttpPost("Leave")] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult LeaveGroup() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -86,8 +93,10 @@ namespace Jellyfin.Api.Controllers /// Gets all SyncPlay groups. /// /// Optional. Filter by item id. + /// Groups returned. /// An containing the available SyncPlay groups. [HttpGet("List")] + [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSyncPlayGroups([FromQuery] Guid? filterItemId) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -97,8 +106,10 @@ namespace Jellyfin.Api.Controllers /// /// Request play in SyncPlay group. /// + /// Play request sent to all group members. /// A indicating success. [HttpPost] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Play() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -113,8 +124,10 @@ namespace Jellyfin.Api.Controllers /// /// Request pause in SyncPlay group. /// + /// Pause request sent to all group members. /// A indicating success. [HttpPost] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Pause() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -130,8 +143,10 @@ namespace Jellyfin.Api.Controllers /// Request seek in SyncPlay group. /// /// The playback position in ticks. + /// Seek request sent to all group members. /// A indicating success. [HttpPost] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Seek([FromQuery] long positionTicks) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -150,8 +165,10 @@ namespace Jellyfin.Api.Controllers /// When the request has been made by the client. /// The playback position in ticks. /// Whether the buffering is done. + /// Buffering request sent to all group members. /// A indicating success. [HttpPost] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Buffering([FromQuery] DateTime when, [FromQuery] long positionTicks, [FromQuery] bool bufferingDone) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -169,8 +186,10 @@ namespace Jellyfin.Api.Controllers /// Update session ping. /// /// The ping. + /// Ping updated. /// A indicating success. [HttpPost] + [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Ping([FromQuery] double ping) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); From 15ac8095b4d7e4b87c420a8789aeaec600827b68 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 22 Jul 2020 16:49:52 +0200 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/SyncPlayController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index 3f40c7309b..c240960e7b 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers /// /// Play request sent to all group members. /// A indicating success. - [HttpPost] + [HttpPost("Play")] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Play() { @@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers /// /// Pause request sent to all group members. /// A indicating success. - [HttpPost] + [HttpPost("Pause")] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Pause() { @@ -145,7 +145,7 @@ namespace Jellyfin.Api.Controllers /// The playback position in ticks. /// Seek request sent to all group members. /// A indicating success. - [HttpPost] + [HttpPost("Seek")] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Seek([FromQuery] long positionTicks) { @@ -167,7 +167,7 @@ namespace Jellyfin.Api.Controllers /// Whether the buffering is done. /// Buffering request sent to all group members. /// A indicating success. - [HttpPost] + [HttpPost("Buffering")] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Buffering([FromQuery] DateTime when, [FromQuery] long positionTicks, [FromQuery] bool bufferingDone) { From 69e6dd2747df84dd732ecf89fea9118085f064ea Mon Sep 17 00:00:00 2001 From: David Date: Wed, 22 Jul 2020 16:53:56 +0200 Subject: [PATCH 5/5] Update Jellyfin.Api/Controllers/SyncPlayController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/SyncPlayController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index c240960e7b..55ed42227d 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -188,7 +188,7 @@ namespace Jellyfin.Api.Controllers /// The ping. /// Ping updated. /// A indicating success. - [HttpPost] + [HttpPost("Ping")] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult Ping([FromQuery] double ping) {