< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.SessionController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/SessionController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 20
Coverable lines: 20
Total lines: 453
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 2
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
GetSessions(...)0%620%
AddUserToSession(...)100%210%
RemoveUserFromSession(...)100%210%
GetAuthProviders()100%210%
GetPasswordResetProviders()100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Api/Controllers/SessionController.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel.DataAnnotations;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Jellyfin.Api.Extensions;
 7using Jellyfin.Api.Helpers;
 8using Jellyfin.Api.ModelBinders;
 9using Jellyfin.Data.Enums;
 10using MediaBrowser.Common.Api;
 11using MediaBrowser.Controller.Library;
 12using MediaBrowser.Controller.Session;
 13using MediaBrowser.Model.Dto;
 14using MediaBrowser.Model.Entities;
 15using MediaBrowser.Model.Session;
 16using Microsoft.AspNetCore.Authorization;
 17using Microsoft.AspNetCore.Http;
 18using Microsoft.AspNetCore.Mvc;
 19
 20namespace Jellyfin.Api.Controllers;
 21
 22/// <summary>
 23/// The session controller.
 24/// </summary>
 25[Route("")]
 26public class SessionController : BaseJellyfinApiController
 27{
 28    private readonly ISessionManager _sessionManager;
 29    private readonly IUserManager _userManager;
 30
 31    /// <summary>
 32    /// Initializes a new instance of the <see cref="SessionController"/> class.
 33    /// </summary>
 34    /// <param name="sessionManager">Instance of <see cref="ISessionManager"/> interface.</param>
 35    /// <param name="userManager">Instance of <see cref="IUserManager"/> interface.</param>
 036    public SessionController(
 037        ISessionManager sessionManager,
 038        IUserManager userManager)
 39    {
 040        _sessionManager = sessionManager;
 041        _userManager = userManager;
 042    }
 43
 44    /// <summary>
 45    /// Gets a list of sessions.
 46    /// </summary>
 47    /// <param name="controllableByUserId">Filter by sessions that a given user is allowed to remote control.</param>
 48    /// <param name="deviceId">Filter by device Id.</param>
 49    /// <param name="activeWithinSeconds">Optional. Filter by sessions that were active in the last n seconds.</param>
 50    /// <response code="200">List of sessions returned.</response>
 51    /// <returns>An <see cref="IReadOnlyList{SessionInfoDto}"/> with the available sessions.</returns>
 52    [HttpGet("Sessions")]
 53    [Authorize]
 54    [ProducesResponseType(StatusCodes.Status200OK)]
 55    public ActionResult<IReadOnlyList<SessionInfoDto>> GetSessions(
 56        [FromQuery] Guid? controllableByUserId,
 57        [FromQuery] string? deviceId,
 58        [FromQuery] int? activeWithinSeconds)
 59    {
 060        Guid? controllableUserToCheck = controllableByUserId is null ? null : RequestHelpers.GetUserId(User, controllabl
 061        var result = _sessionManager.GetSessions(
 062            User.GetUserId(),
 063            deviceId,
 064            activeWithinSeconds,
 065            controllableUserToCheck,
 066            User.GetIsApiKey());
 67
 068        return Ok(result);
 69    }
 70
 71    /// <summary>
 72    /// Instructs a session to browse to an item or view.
 73    /// </summary>
 74    /// <param name="sessionId">The session Id.</param>
 75    /// <param name="itemType">The type of item to browse to.</param>
 76    /// <param name="itemId">The Id of the item.</param>
 77    /// <param name="itemName">The name of the item.</param>
 78    /// <response code="204">Instruction sent to session.</response>
 79    /// <returns>A <see cref="NoContentResult"/>.</returns>
 80    [HttpPost("Sessions/{sessionId}/Viewing")]
 81    [Authorize]
 82    [ProducesResponseType(StatusCodes.Status204NoContent)]
 83    public async Task<ActionResult> DisplayContent(
 84        [FromRoute, Required] string sessionId,
 85        [FromQuery, Required] BaseItemKind itemType,
 86        [FromQuery, Required] string itemId,
 87        [FromQuery, Required] string itemName)
 88    {
 89        var command = new BrowseRequest
 90        {
 91            ItemId = itemId,
 92            ItemName = itemName,
 93            ItemType = itemType
 94        };
 95
 96        await _sessionManager.SendBrowseCommand(
 97            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 98            sessionId,
 99            command,
 100            CancellationToken.None)
 101            .ConfigureAwait(false);
 102
 103        return NoContent();
 104    }
 105
 106    /// <summary>
 107    /// Instructs a session to play an item.
 108    /// </summary>
 109    /// <param name="sessionId">The session id.</param>
 110    /// <param name="playCommand">The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not 
 111    /// <param name="itemIds">The ids of the items to play, comma delimited.</param>
 112    /// <param name="startPositionTicks">The starting position of the first item.</param>
 113    /// <param name="mediaSourceId">Optional. The media source id.</param>
 114    /// <param name="audioStreamIndex">Optional. The index of the audio stream to play.</param>
 115    /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to play.</param>
 116    /// <param name="startIndex">Optional. The start index.</param>
 117    /// <response code="204">Instruction sent to session.</response>
 118    /// <returns>A <see cref="NoContentResult"/>.</returns>
 119    [HttpPost("Sessions/{sessionId}/Playing")]
 120    [Authorize]
 121    [ProducesResponseType(StatusCodes.Status204NoContent)]
 122    public async Task<ActionResult> Play(
 123        [FromRoute, Required] string sessionId,
 124        [FromQuery, Required] PlayCommand playCommand,
 125        [FromQuery, Required, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] itemIds,
 126        [FromQuery] long? startPositionTicks,
 127        [FromQuery] string? mediaSourceId,
 128        [FromQuery] int? audioStreamIndex,
 129        [FromQuery] int? subtitleStreamIndex,
 130        [FromQuery] int? startIndex)
 131    {
 132        var playRequest = new PlayRequest
 133        {
 134            ItemIds = itemIds,
 135            StartPositionTicks = startPositionTicks,
 136            PlayCommand = playCommand,
 137            MediaSourceId = mediaSourceId,
 138            AudioStreamIndex = audioStreamIndex,
 139            SubtitleStreamIndex = subtitleStreamIndex,
 140            StartIndex = startIndex
 141        };
 142
 143        await _sessionManager.SendPlayCommand(
 144            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 145            sessionId,
 146            playRequest,
 147            CancellationToken.None)
 148            .ConfigureAwait(false);
 149
 150        return NoContent();
 151    }
 152
 153    /// <summary>
 154    /// Issues a playstate command to a client.
 155    /// </summary>
 156    /// <param name="sessionId">The session id.</param>
 157    /// <param name="command">The <see cref="PlaystateCommand"/>.</param>
 158    /// <param name="seekPositionTicks">The optional position ticks.</param>
 159    /// <param name="controllingUserId">The optional controlling user id.</param>
 160    /// <response code="204">Playstate command sent to session.</response>
 161    /// <returns>A <see cref="NoContentResult"/>.</returns>
 162    [HttpPost("Sessions/{sessionId}/Playing/{command}")]
 163    [Authorize]
 164    [ProducesResponseType(StatusCodes.Status204NoContent)]
 165    public async Task<ActionResult> SendPlaystateCommand(
 166        [FromRoute, Required] string sessionId,
 167        [FromRoute, Required] PlaystateCommand command,
 168        [FromQuery] long? seekPositionTicks,
 169        [FromQuery] string? controllingUserId)
 170    {
 171        await _sessionManager.SendPlaystateCommand(
 172            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 173            sessionId,
 174            new PlaystateRequest()
 175            {
 176                Command = command,
 177                ControllingUserId = controllingUserId,
 178                SeekPositionTicks = seekPositionTicks,
 179            },
 180            CancellationToken.None)
 181            .ConfigureAwait(false);
 182
 183        return NoContent();
 184    }
 185
 186    /// <summary>
 187    /// Issues a system command to a client.
 188    /// </summary>
 189    /// <param name="sessionId">The session id.</param>
 190    /// <param name="command">The command to send.</param>
 191    /// <response code="204">System command sent to session.</response>
 192    /// <returns>A <see cref="NoContentResult"/>.</returns>
 193    [HttpPost("Sessions/{sessionId}/System/{command}")]
 194    [Authorize]
 195    [ProducesResponseType(StatusCodes.Status204NoContent)]
 196    public async Task<ActionResult> SendSystemCommand(
 197        [FromRoute, Required] string sessionId,
 198        [FromRoute, Required] GeneralCommandType command)
 199    {
 200        var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(
 201        var generalCommand = new GeneralCommand
 202        {
 203            Name = command,
 204            ControllingUserId = currentSession.UserId
 205        };
 206
 207        await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None).C
 208
 209        return NoContent();
 210    }
 211
 212    /// <summary>
 213    /// Issues a general command to a client.
 214    /// </summary>
 215    /// <param name="sessionId">The session id.</param>
 216    /// <param name="command">The command to send.</param>
 217    /// <response code="204">General command sent to session.</response>
 218    /// <returns>A <see cref="NoContentResult"/>.</returns>
 219    [HttpPost("Sessions/{sessionId}/Command/{command}")]
 220    [Authorize]
 221    [ProducesResponseType(StatusCodes.Status204NoContent)]
 222    public async Task<ActionResult> SendGeneralCommand(
 223        [FromRoute, Required] string sessionId,
 224        [FromRoute, Required] GeneralCommandType command)
 225    {
 226        var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(
 227
 228        var generalCommand = new GeneralCommand
 229        {
 230            Name = command,
 231            ControllingUserId = currentSession.UserId
 232        };
 233
 234        await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None)
 235            .ConfigureAwait(false);
 236
 237        return NoContent();
 238    }
 239
 240    /// <summary>
 241    /// Issues a full general command to a client.
 242    /// </summary>
 243    /// <param name="sessionId">The session id.</param>
 244    /// <param name="command">The <see cref="GeneralCommand"/>.</param>
 245    /// <response code="204">Full general command sent to session.</response>
 246    /// <returns>A <see cref="NoContentResult"/>.</returns>
 247    [HttpPost("Sessions/{sessionId}/Command")]
 248    [Authorize]
 249    [ProducesResponseType(StatusCodes.Status204NoContent)]
 250    public async Task<ActionResult> SendFullGeneralCommand(
 251        [FromRoute, Required] string sessionId,
 252        [FromBody, Required] GeneralCommand command)
 253    {
 254        var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(
 255
 256        ArgumentNullException.ThrowIfNull(command);
 257
 258        command.ControllingUserId = currentSession.UserId;
 259
 260        await _sessionManager.SendGeneralCommand(
 261            currentSession.Id,
 262            sessionId,
 263            command,
 264            CancellationToken.None)
 265            .ConfigureAwait(false);
 266
 267        return NoContent();
 268    }
 269
 270    /// <summary>
 271    /// Issues a command to a client to display a message to the user.
 272    /// </summary>
 273    /// <param name="sessionId">The session id.</param>
 274    /// <param name="command">The <see cref="MessageCommand" /> object containing Header, Message Text, and TimeoutMs.</
 275    /// <response code="204">Message sent.</response>
 276    /// <returns>A <see cref="NoContentResult"/>.</returns>
 277    [HttpPost("Sessions/{sessionId}/Message")]
 278    [Authorize]
 279    [ProducesResponseType(StatusCodes.Status204NoContent)]
 280    public async Task<ActionResult> SendMessageCommand(
 281        [FromRoute, Required] string sessionId,
 282        [FromBody, Required] MessageCommand command)
 283    {
 284        if (string.IsNullOrWhiteSpace(command.Header))
 285        {
 286            command.Header = "Message from Server";
 287        }
 288
 289        await _sessionManager.SendMessageCommand(
 290            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 291            sessionId,
 292            command,
 293            CancellationToken.None)
 294            .ConfigureAwait(false);
 295
 296        return NoContent();
 297    }
 298
 299    /// <summary>
 300    /// Adds an additional user to a session.
 301    /// </summary>
 302    /// <param name="sessionId">The session id.</param>
 303    /// <param name="userId">The user id.</param>
 304    /// <response code="204">User added to session.</response>
 305    /// <returns>A <see cref="NoContentResult"/>.</returns>
 306    [HttpPost("Sessions/{sessionId}/User/{userId}")]
 307    [Authorize]
 308    [ProducesResponseType(StatusCodes.Status204NoContent)]
 309    public ActionResult AddUserToSession(
 310        [FromRoute, Required] string sessionId,
 311        [FromRoute, Required] Guid userId)
 312    {
 0313        _sessionManager.AddAdditionalUser(sessionId, userId);
 0314        return NoContent();
 315    }
 316
 317    /// <summary>
 318    /// Removes an additional user from a session.
 319    /// </summary>
 320    /// <param name="sessionId">The session id.</param>
 321    /// <param name="userId">The user id.</param>
 322    /// <response code="204">User removed from session.</response>
 323    /// <returns>A <see cref="NoContentResult"/>.</returns>
 324    [HttpDelete("Sessions/{sessionId}/User/{userId}")]
 325    [Authorize]
 326    [ProducesResponseType(StatusCodes.Status204NoContent)]
 327    public ActionResult RemoveUserFromSession(
 328        [FromRoute, Required] string sessionId,
 329        [FromRoute, Required] Guid userId)
 330    {
 0331        _sessionManager.RemoveAdditionalUser(sessionId, userId);
 0332        return NoContent();
 333    }
 334
 335    /// <summary>
 336    /// Updates capabilities for a device.
 337    /// </summary>
 338    /// <param name="id">The session id.</param>
 339    /// <param name="playableMediaTypes">A list of playable media types, comma delimited. Audio, Video, Book, Photo.</pa
 340    /// <param name="supportedCommands">A list of supported remote control commands, comma delimited.</param>
 341    /// <param name="supportsMediaControl">Determines whether media can be played remotely..</param>
 342    /// <param name="supportsPersistentIdentifier">Determines whether the device supports a unique identifier.</param>
 343    /// <response code="204">Capabilities posted.</response>
 344    /// <returns>A <see cref="NoContentResult"/>.</returns>
 345    [HttpPost("Sessions/Capabilities")]
 346    [Authorize]
 347    [ProducesResponseType(StatusCodes.Status204NoContent)]
 348    public async Task<ActionResult> PostCapabilities(
 349        [FromQuery] string? id,
 350        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] playableMediaTypes,
 351        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] GeneralCommandType[] supportedCommands,
 352        [FromQuery] bool supportsMediaControl = false,
 353        [FromQuery] bool supportsPersistentIdentifier = true)
 354    {
 355        if (string.IsNullOrWhiteSpace(id))
 356        {
 357            id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
 358        }
 359
 360        _sessionManager.ReportCapabilities(id, new ClientCapabilities
 361        {
 362            PlayableMediaTypes = playableMediaTypes,
 363            SupportedCommands = supportedCommands,
 364            SupportsMediaControl = supportsMediaControl,
 365            SupportsPersistentIdentifier = supportsPersistentIdentifier
 366        });
 367        return NoContent();
 368    }
 369
 370    /// <summary>
 371    /// Updates capabilities for a device.
 372    /// </summary>
 373    /// <param name="id">The session id.</param>
 374    /// <param name="capabilities">The <see cref="ClientCapabilities"/>.</param>
 375    /// <response code="204">Capabilities updated.</response>
 376    /// <returns>A <see cref="NoContentResult"/>.</returns>
 377    [HttpPost("Sessions/Capabilities/Full")]
 378    [Authorize]
 379    [ProducesResponseType(StatusCodes.Status204NoContent)]
 380    public async Task<ActionResult> PostFullCapabilities(
 381        [FromQuery] string? id,
 382        [FromBody, Required] ClientCapabilitiesDto capabilities)
 383    {
 384        if (string.IsNullOrWhiteSpace(id))
 385        {
 386            id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
 387        }
 388
 389        _sessionManager.ReportCapabilities(id, capabilities.ToClientCapabilities());
 390
 391        return NoContent();
 392    }
 393
 394    /// <summary>
 395    /// Reports that a session is viewing an item.
 396    /// </summary>
 397    /// <param name="sessionId">The session id.</param>
 398    /// <param name="itemId">The item id.</param>
 399    /// <response code="204">Session reported to server.</response>
 400    /// <returns>A <see cref="NoContentResult"/>.</returns>
 401    [HttpPost("Sessions/Viewing")]
 402    [Authorize]
 403    [ProducesResponseType(StatusCodes.Status204NoContent)]
 404    public async Task<ActionResult> ReportViewing(
 405        [FromQuery] string? sessionId,
 406        [FromQuery, Required] string? itemId)
 407    {
 408        string session = sessionId ?? await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).Conf
 409
 410        _sessionManager.ReportNowViewingItem(session, itemId);
 411        return NoContent();
 412    }
 413
 414    /// <summary>
 415    /// Reports that a session has ended.
 416    /// </summary>
 417    /// <response code="204">Session end reported to server.</response>
 418    /// <returns>A <see cref="NoContentResult"/>.</returns>
 419    [HttpPost("Sessions/Logout")]
 420    [Authorize]
 421    [ProducesResponseType(StatusCodes.Status204NoContent)]
 422    public async Task<ActionResult> ReportSessionEnded()
 423    {
 424        await _sessionManager.Logout(User.GetToken()).ConfigureAwait(false);
 425        return NoContent();
 426    }
 427
 428    /// <summary>
 429    /// Get all auth providers.
 430    /// </summary>
 431    /// <response code="200">Auth providers retrieved.</response>
 432    /// <returns>An <see cref="IEnumerable{NameIdPair}"/> with the auth providers.</returns>
 433    [HttpGet("Auth/Providers")]
 434    [Authorize(Policy = Policies.RequiresElevation)]
 435    [ProducesResponseType(StatusCodes.Status200OK)]
 436    public ActionResult<IEnumerable<NameIdPair>> GetAuthProviders()
 437    {
 0438        return _userManager.GetAuthenticationProviders();
 439    }
 440
 441    /// <summary>
 442    /// Get all password reset providers.
 443    /// </summary>
 444    /// <response code="200">Password reset providers retrieved.</response>
 445    /// <returns>An <see cref="IEnumerable{NameIdPair}"/> with the password reset providers.</returns>
 446    [HttpGet("Auth/PasswordResetProviders")]
 447    [ProducesResponseType(StatusCodes.Status200OK)]
 448    [Authorize(Policy = Policies.RequiresElevation)]
 449    public ActionResult<IEnumerable<NameIdPair>> GetPasswordResetProviders()
 450    {
 0451        return _userManager.GetPasswordResetProviders();
 452    }
 453}