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.Http; 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. /// /// New group created. /// A indicating success. [HttpPost("New")] [ProducesResponseType(StatusCodes.Status204NoContent)] 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. /// 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); var joinRequest = new JoinGroupRequest() { GroupId = groupId }; _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None); return NoContent(); } /// /// 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); _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None); return NoContent(); } /// /// 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); return Ok(_syncPlayManager.ListGroups(currentSession, filterItemId.HasValue ? filterItemId.Value : Guid.Empty)); } /// /// Request play in SyncPlay group. /// /// Play request sent to all group members. /// A indicating success. [HttpPost("Play")] [ProducesResponseType(StatusCodes.Status204NoContent)] 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. /// /// Pause request sent to all group members. /// A indicating success. [HttpPost("Pause")] [ProducesResponseType(StatusCodes.Status204NoContent)] 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. /// Seek request sent to all group members. /// A indicating success. [HttpPost("Seek")] [ProducesResponseType(StatusCodes.Status204NoContent)] 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. /// Buffering request sent to all group members. /// A indicating success. [HttpPost("Buffering")] [ProducesResponseType(StatusCodes.Status204NoContent)] 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.Ready : PlaybackRequestType.Buffer, When = when, PositionTicks = positionTicks }; _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); } /// /// 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); var syncPlayRequest = new PlaybackRequest() { Type = PlaybackRequestType.Ping, Ping = Convert.ToInt64(ping) }; _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); } } }