< 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: 128
Coverable lines: 128
Total lines: 453
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 0% (0/20) Branch coverage: 0% (0/2) Total lines: 4534/19/2026 - 12:14:27 AM Line coverage: 0% (0/128) Branch coverage: 0% (0/10) Total lines: 453 1/23/2026 - 12:11:06 AM Line coverage: 0% (0/20) Branch coverage: 0% (0/2) Total lines: 4534/19/2026 - 12:14:27 AM Line coverage: 0% (0/128) Branch coverage: 0% (0/10) Total lines: 453

Coverage delta

Coverage delta 1 -1

Metrics

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    {
 089        var command = new BrowseRequest
 090        {
 091            ItemId = itemId,
 092            ItemName = itemName,
 093            ItemType = itemType
 094        };
 95
 096        await _sessionManager.SendBrowseCommand(
 097            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 098            sessionId,
 099            command,
 0100            CancellationToken.None)
 0101            .ConfigureAwait(false);
 102
 0103        return NoContent();
 0104    }
 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    {
 0132        var playRequest = new PlayRequest
 0133        {
 0134            ItemIds = itemIds,
 0135            StartPositionTicks = startPositionTicks,
 0136            PlayCommand = playCommand,
 0137            MediaSourceId = mediaSourceId,
 0138            AudioStreamIndex = audioStreamIndex,
 0139            SubtitleStreamIndex = subtitleStreamIndex,
 0140            StartIndex = startIndex
 0141        };
 142
 0143        await _sessionManager.SendPlayCommand(
 0144            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 0145            sessionId,
 0146            playRequest,
 0147            CancellationToken.None)
 0148            .ConfigureAwait(false);
 149
 0150        return NoContent();
 0151    }
 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    {
 0171        await _sessionManager.SendPlaystateCommand(
 0172            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 0173            sessionId,
 0174            new PlaystateRequest()
 0175            {
 0176                Command = command,
 0177                ControllingUserId = controllingUserId,
 0178                SeekPositionTicks = seekPositionTicks,
 0179            },
 0180            CancellationToken.None)
 0181            .ConfigureAwait(false);
 182
 0183        return NoContent();
 0184    }
 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    {
 0200        var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(
 0201        var generalCommand = new GeneralCommand
 0202        {
 0203            Name = command,
 0204            ControllingUserId = currentSession.UserId
 0205        };
 206
 0207        await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None).C
 208
 0209        return NoContent();
 0210    }
 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    {
 0226        var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(
 227
 0228        var generalCommand = new GeneralCommand
 0229        {
 0230            Name = command,
 0231            ControllingUserId = currentSession.UserId
 0232        };
 233
 0234        await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None)
 0235            .ConfigureAwait(false);
 236
 0237        return NoContent();
 0238    }
 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    {
 0254        var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(
 255
 0256        ArgumentNullException.ThrowIfNull(command);
 257
 0258        command.ControllingUserId = currentSession.UserId;
 259
 0260        await _sessionManager.SendGeneralCommand(
 0261            currentSession.Id,
 0262            sessionId,
 0263            command,
 0264            CancellationToken.None)
 0265            .ConfigureAwait(false);
 266
 0267        return NoContent();
 0268    }
 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    {
 0284        if (string.IsNullOrWhiteSpace(command.Header))
 285        {
 0286            command.Header = "Message from Server";
 287        }
 288
 0289        await _sessionManager.SendMessageCommand(
 0290            await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
 0291            sessionId,
 0292            command,
 0293            CancellationToken.None)
 0294            .ConfigureAwait(false);
 295
 0296        return NoContent();
 0297    }
 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    {
 0355        if (string.IsNullOrWhiteSpace(id))
 356        {
 0357            id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
 358        }
 359
 0360        _sessionManager.ReportCapabilities(id, new ClientCapabilities
 0361        {
 0362            PlayableMediaTypes = playableMediaTypes,
 0363            SupportedCommands = supportedCommands,
 0364            SupportsMediaControl = supportsMediaControl,
 0365            SupportsPersistentIdentifier = supportsPersistentIdentifier
 0366        });
 0367        return NoContent();
 0368    }
 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    {
 0384        if (string.IsNullOrWhiteSpace(id))
 385        {
 0386            id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
 387        }
 388
 0389        _sessionManager.ReportCapabilities(id, capabilities.ToClientCapabilities());
 390
 0391        return NoContent();
 0392    }
 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    {
 0408        string session = sessionId ?? await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).Conf
 409
 0410        _sessionManager.ReportNowViewingItem(session, itemId);
 0411        return NoContent();
 0412    }
 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    {
 0424        await _sessionManager.Logout(User.GetToken()).ConfigureAwait(false);
 0425        return NoContent();
 0426    }
 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}