From fff3c789b98aad08f8ea66da275ff82c71dd8f2b Mon Sep 17 00:00:00 2001 From: David Date: Fri, 12 Jun 2020 18:54:25 +0200 Subject: Move SessionService to Jellyfin.Api --- Jellyfin.Api/Controllers/SessionController.cs | 478 ++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) create mode 100644 Jellyfin.Api/Controllers/SessionController.cs (limited to 'Jellyfin.Api/Controllers/SessionController.cs') diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs new file mode 100644 index 000000000..5b60275eb --- /dev/null +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -0,0 +1,478 @@ +#pragma warning disable CA1801 + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Session; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers +{ + /// + /// The session controller. + /// + public class SessionController : BaseJellyfinApiController + { + private readonly ISessionManager _sessionManager; + private readonly IUserManager _userManager; + private readonly IAuthorizationContext _authContext; + private readonly IDeviceManager _deviceManager; + private readonly ISessionContext _sessionContext; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of interface. + /// Instance of interface. + /// Instance of interface. + /// Instance of interface. + /// Instance of interface. + public SessionController( + ISessionManager sessionManager, + IUserManager userManager, + IAuthorizationContext authContext, + IDeviceManager deviceManager, + ISessionContext sessionContext) + { + _sessionManager = sessionManager; + _userManager = userManager; + _authContext = authContext; + _deviceManager = deviceManager; + _sessionContext = sessionContext; + } + + /// + /// Gets a list of sessions. + /// + /// Filter by sessions that a given user is allowed to remote control. + /// Filter by device Id. + /// Optional. Filter by sessions that were active in the last n seconds. + /// List of sessions returned. + /// An with the available sessions. + [HttpGet("/Sessions")] + [Authorize] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetSessions( + [FromQuery] Guid controllableByUserId, + [FromQuery] string deviceId, + [FromQuery] int? activeWithinSeconds) + { + var result = _sessionManager.Sessions; + + if (!string.IsNullOrEmpty(deviceId)) + { + result = result.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); + } + + if (!controllableByUserId.Equals(Guid.Empty)) + { + result = result.Where(i => i.SupportsRemoteControl); + + var user = _userManager.GetUserById(controllableByUserId); + + if (!user.Policy.EnableRemoteControlOfOtherUsers) + { + result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(controllableByUserId)); + } + + if (!user.Policy.EnableSharedDeviceControl) + { + result = result.Where(i => !i.UserId.Equals(Guid.Empty)); + } + + if (activeWithinSeconds.HasValue && activeWithinSeconds.Value > 0) + { + var minActiveDate = DateTime.UtcNow.AddSeconds(0 - activeWithinSeconds.Value); + result = result.Where(i => i.LastActivityDate >= minActiveDate); + } + + result = result.Where(i => + { + if (!string.IsNullOrWhiteSpace(i.DeviceId)) + { + if (!_deviceManager.CanAccessDevice(user, i.DeviceId)) + { + return false; + } + } + + return true; + }); + } + + return Ok(result); + } + + /// + /// Instructs a session to browse to an item or view. + /// + /// The session Id. + /// The type of item to browse to. + /// The Id of the item. + /// The name of the item. + /// Instruction sent to session. + /// A . + [HttpPost("/Sessions/{id}/Viewing")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult DisplayContent( + [FromRoute] string id, + [FromQuery] string itemType, + [FromQuery] string itemId, + [FromQuery] string itemName) + { + var command = new BrowseRequest + { + ItemId = itemId, + ItemName = itemName, + ItemType = itemType + }; + + _sessionManager.SendBrowseCommand( + RequestHelpers.GetSession(_sessionContext).Id, + id, + command, + CancellationToken.None); + + return NoContent(); + } + + /// + /// Instructs a session to play an item. + /// + /// The session id. + /// The ids of the items to play, comma delimited. + /// The starting position of the first item. + /// The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now. + /// The . + /// Instruction sent to session. + /// A . + [HttpPost("/Sessions/{id}/Playing")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult Play( + [FromRoute] string id, + [FromQuery] Guid[] itemIds, + [FromQuery] long? startPositionTicks, + [FromQuery] PlayCommand playCommand, + [FromBody, Required] PlayRequest playRequest) + { + if (playRequest == null) + { + throw new ArgumentException("Request Body may not be null"); + } + + playRequest.ItemIds = itemIds; + playRequest.StartPositionTicks = startPositionTicks; + playRequest.PlayCommand = playCommand; + + _sessionManager.SendPlayCommand( + RequestHelpers.GetSession(_sessionContext).Id, + id, + playRequest, + CancellationToken.None); + + return NoContent(); + } + + /// + /// Issues a playstate command to a client. + /// + /// The session id. + /// The . + /// Playstate command sent to session. + /// A . + [HttpPost("/Sessions/{id}/Playing/{command}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult SendPlaystateCommand( + [FromRoute] string id, + [FromBody] PlaystateRequest playstateRequest) + { + _sessionManager.SendPlaystateCommand( + RequestHelpers.GetSession(_sessionContext).Id, + id, + playstateRequest, + CancellationToken.None); + + return NoContent(); + } + + /// + /// Issues a system command to a client. + /// + /// The session id. + /// The command to send. + /// System command sent to session. + /// A . + [HttpPost("/Sessions/{id}/System/{Command}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult SendSystemCommand( + [FromRoute] string id, + [FromRoute] string command) + { + var name = command; + if (Enum.TryParse(name, true, out GeneralCommandType commandType)) + { + name = commandType.ToString(); + } + + var currentSession = RequestHelpers.GetSession(_sessionContext); + var generalCommand = new GeneralCommand + { + Name = name, + ControllingUserId = currentSession.UserId + }; + + _sessionManager.SendGeneralCommand(currentSession.Id, id, generalCommand, CancellationToken.None); + + return NoContent(); + } + + /// + /// Issues a general command to a client. + /// + /// The session id. + /// The command to send. + /// General command sent to session. + /// A . + [HttpPost("/Sessions/{id}/Command/{Command}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult SendGeneralCommand( + [FromRoute] string id, + [FromRoute] string command) + { + var currentSession = RequestHelpers.GetSession(_sessionContext); + + var generalCommand = new GeneralCommand + { + Name = command, + ControllingUserId = currentSession.UserId + }; + + _sessionManager.SendGeneralCommand(currentSession.Id, id, generalCommand, CancellationToken.None); + + return NoContent(); + } + + /// + /// Issues a full general command to a client. + /// + /// The session id. + /// The . + /// Full general command sent to session. + /// A . + [HttpPost("/Sessions/{id}/Command")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult SendFullGeneralCommand( + [FromRoute] string id, + [FromBody, Required] GeneralCommand command) + { + var currentSession = RequestHelpers.GetSession(_sessionContext); + + if (command == null) + { + throw new ArgumentException("Request body may not be null"); + } + + command.ControllingUserId = currentSession.UserId; + + _sessionManager.SendGeneralCommand( + currentSession.Id, + id, + command, + CancellationToken.None); + + return NoContent(); + } + + /// + /// Issues a command to a client to display a message to the user. + /// + /// The session id. + /// The message test. + /// The message header. + /// The message timeout. If omitted the user will have to confirm viewing the message. + /// Message sent. + /// A . + [HttpPost("/Sessions/{id}/Message")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult SendMessageCommand( + [FromRoute] string id, + [FromQuery] string text, + [FromQuery] string header, + [FromQuery] long? timeoutMs) + { + var command = new MessageCommand + { + Header = string.IsNullOrEmpty(header) ? "Message from Server" : header, + TimeoutMs = timeoutMs, + Text = text + }; + + _sessionManager.SendMessageCommand(RequestHelpers.GetSession(_sessionContext).Id, id, command, CancellationToken.None); + + return NoContent(); + } + + /// + /// Adds an additional user to a session. + /// + /// The session id. + /// The user id. + /// User added to session. + /// A . + [HttpPost("/Sessions/{id}/User/{userId}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult AddUserToSession( + [FromRoute] string id, + [FromRoute] Guid userId) + { + _sessionManager.AddAdditionalUser(id, userId); + return NoContent(); + } + + /// + /// Removes an additional user from a session. + /// + /// The session id. + /// The user id. + /// User removed from session. + /// A . + [HttpDelete("/Sessions/{id}/User/{userId}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult RemoveUserFromSession( + [FromRoute] string id, + [FromRoute] Guid userId) + { + _sessionManager.RemoveAdditionalUser(id, userId); + return NoContent(); + } + + /// + /// Updates capabilities for a device. + /// + /// The session id. + /// A list of playable media types, comma delimited. Audio, Video, Book, Photo. + /// A list of supported remote control commands, comma delimited. + /// Determines whether media can be played remotely.. + /// Determines whether sync is supported. + /// Determines whether the device supports a unique identifier. + /// Capabilities posted. + /// A . + [HttpPost("/Sessions/Capabilities")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult PostCapabilities( + [FromQuery] string id, + [FromQuery] string playableMediaTypes, + [FromQuery] string supportedCommands, + [FromQuery] bool supportsMediaControl, + [FromQuery] bool supportsSync, + [FromQuery] bool supportsPersistentIdentifier = true) + { + if (string.IsNullOrWhiteSpace(id)) + { + id = RequestHelpers.GetSession(_sessionContext).Id; + } + + _sessionManager.ReportCapabilities(id, new ClientCapabilities + { + PlayableMediaTypes = RequestHelpers.Split(playableMediaTypes, ',', true), + SupportedCommands = RequestHelpers.Split(supportedCommands, ',', true), + SupportsMediaControl = supportsMediaControl, + SupportsSync = supportsSync, + SupportsPersistentIdentifier = supportsPersistentIdentifier + }); + return NoContent(); + } + + /// + /// Updates capabilities for a device. + /// + /// The session id. + /// The . + /// Capabilities updated. + /// A . + [HttpPost("/Sessions/Capabilities/Full")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult PostFullCapabilities( + [FromQuery] string id, + [FromBody, Required] ClientCapabilities capabilities) + { + if (string.IsNullOrWhiteSpace(id)) + { + id = RequestHelpers.GetSession(_sessionContext).Id; + } + + _sessionManager.ReportCapabilities(id, capabilities); + + return NoContent(); + } + + /// + /// Reports that a session is viewing an item. + /// + /// The session id. + /// The item id. + /// Session reported to server. + /// A . + [HttpPost("/Sessions/Viewing")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult ReportViewing( + [FromQuery] string sessionId, + [FromQuery] string itemId) + { + string session = RequestHelpers.GetSession(_sessionContext).Id; + + _sessionManager.ReportNowViewingItem(session, itemId); + return NoContent(); + } + + /// + /// Reports that a session has ended. + /// + /// Session end reported to server. + /// A . + [HttpPost("/Sessions/Logout")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public ActionResult ReportSessionEnded() + { + // TODO: how do we get AuthorizationInfo without an IRequest? + AuthorizationInfo auth = _authContext.GetAuthorizationInfo(Request); + + _sessionManager.Logout(auth.Token); + return NoContent(); + } + + /// + /// Get all auth providers. + /// + /// Auth providers retrieved. + /// An with the auth providers. + [HttpGet("/Auth/Providers")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetAuthProviders() + { + return _userManager.GetAuthenticationProviders(); + } + + /// + /// Get all password reset providers. + /// + /// Password reset providers retrieved. + /// An with the password reset providers. + [HttpGet("/Auto/PasswordResetProviders")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetPasswordResetProviders() + { + return _userManager.GetPasswordResetProviders(); + } + } +} -- cgit v1.2.3