< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.PersonsController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/PersonsController.cs
Line coverage
25%
Covered lines: 13
Uncovered lines: 38
Coverable lines: 51
Total lines: 173
Line coverage: 25.4%
Branch coverage
7%
Covered branches: 1
Total branches: 14
Branch coverage: 7.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/6/2026 - 12:15:23 AM Line coverage: 28.8% (13/45) Branch coverage: 10% (1/10) Total lines: 1578/6/2026 - 12:17:15 AM Line coverage: 25.4% (13/51) Branch coverage: 7.1% (1/14) Total lines: 173 5/6/2026 - 12:15:23 AM Line coverage: 28.8% (13/45) Branch coverage: 10% (1/10) Total lines: 1578/6/2026 - 12:17:15 AM Line coverage: 25.4% (13/51) Branch coverage: 7.1% (1/14) Total lines: 173

Coverage delta

Coverage delta 4 -4

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetPersons(...)0%4260%
BuildAccessFilter(...)0%2040%
GetPerson(...)25%5455.55%

File(s)

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

#LineLine coverage
 1using System;
 2using System.ComponentModel.DataAnnotations;
 3using System.Linq;
 4using Jellyfin.Api.Extensions;
 5using Jellyfin.Api.Helpers;
 6using Jellyfin.Api.ModelBinders;
 7using Jellyfin.Data;
 8using Jellyfin.Database.Implementations.Entities;
 9using Jellyfin.Extensions;
 10using MediaBrowser.Controller.Dto;
 11using MediaBrowser.Controller.Entities;
 12using MediaBrowser.Controller.Library;
 13using MediaBrowser.Model.Dto;
 14using MediaBrowser.Model.Entities;
 15using MediaBrowser.Model.Querying;
 16using Microsoft.AspNetCore.Authorization;
 17using Microsoft.AspNetCore.Http;
 18using Microsoft.AspNetCore.Mvc;
 19
 20namespace Jellyfin.Api.Controllers;
 21
 22/// <summary>
 23/// Persons controller.
 24/// </summary>
 25[Authorize]
 26[Tags("Person")]
 27public class PersonsController : BaseJellyfinApiController
 28{
 29    private readonly ILibraryManager _libraryManager;
 30    private readonly IDtoService _dtoService;
 31    private readonly IUserManager _userManager;
 32
 33    /// <summary>
 34    /// Initializes a new instance of the <see cref="PersonsController"/> class.
 35    /// </summary>
 36    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 37    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 38    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 139    public PersonsController(
 140        ILibraryManager libraryManager,
 141        IDtoService dtoService,
 142        IUserManager userManager)
 43    {
 144        _libraryManager = libraryManager;
 145        _dtoService = dtoService;
 146        _userManager = userManager;
 147    }
 48
 49    /// <summary>
 50    /// Gets all persons.
 51    /// </summary>
 52    /// <param name="startIndex">Optional. All items with a lower index will be dropped from the response.</param>
 53    /// <param name="limit">Optional. The maximum number of records to return.</param>
 54    /// <param name="searchTerm">The search term.</param>
 55    /// <param name="nameStartsWith">Optional. Filter by items whose name starts with the given input string.</param>
 56    /// <param name="nameLessThan">Optional. Filter by items whose name will appear before this value when sorted alphab
 57    /// <param name="nameStartsWithOrGreater">Optional. Filter by items whose name will appear after this value when sor
 58    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 59    /// <param name="filters">Optional. Specify additional filters to apply.</param>
 60    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not. userId is required.</para
 61    /// <param name="enableUserData">Optional, include user data.</param>
 62    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 63    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 64    /// <param name="excludePersonTypes">Optional. If specified results will be filtered to exclude those containing the
 65    /// <param name="personTypes">Optional. If specified results will be filtered to include only those containing the s
 66    /// <param name="parentId">Optional. Specify this to localize the search to a specific library. Omit to use the root
 67    /// <param name="appearsInItemId">Optional. If specified, person results will be filtered on items related to said p
 68    /// <param name="userId">User id.</param>
 69    /// <param name="enableImages">Optional, include image information in output.</param>
 70    /// <response code="200">Persons returned.</response>
 71    /// <returns>An <see cref="OkResult"/> containing the queryresult of persons.</returns>
 72    [HttpGet]
 73    [ProducesResponseType(StatusCodes.Status200OK)]
 74    public ActionResult<QueryResult<BaseItemDto>> GetPersons(
 75        [FromQuery] int? startIndex,
 76        [FromQuery] int? limit,
 77        [FromQuery] string? searchTerm,
 78        [FromQuery] string? nameStartsWith,
 79        [FromQuery] string? nameLessThan,
 80        [FromQuery] string? nameStartsWithOrGreater,
 81        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 82        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFilter[] filters,
 83        [FromQuery] bool? isFavorite,
 84        [FromQuery] bool? enableUserData,
 85        [FromQuery] int? imageTypeLimit,
 86        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 87        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] string[] excludePersonTypes,
 88        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] string[] personTypes,
 89        [FromQuery] Guid? parentId,
 90        [FromQuery] Guid? appearsInItemId,
 91        [FromQuery] Guid? userId,
 92        [FromQuery] bool? enableImages = true)
 93    {
 094        userId = RequestHelpers.GetUserId(User, userId);
 095        var dtoOptions = new DtoOptions { Fields = fields }
 096            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 97
 098        User? user = userId.IsNullOrEmpty()
 099            ? null
 0100            : _userManager.GetUserById(userId.Value);
 101
 0102        var isFavoriteInFilters = filters.Any(f => f == ItemFilter.IsFavorite);
 0103        var peopleItems = _libraryManager.GetPeopleItems(new InternalPeopleQuery(
 0104            personTypes,
 0105            excludePersonTypes)
 0106        {
 0107            AccessFilter = BuildAccessFilter(user),
 0108            NameContains = searchTerm,
 0109            NameStartsWith = nameStartsWith,
 0110            NameLessThan = nameLessThan,
 0111            NameStartsWithOrGreater = nameStartsWithOrGreater,
 0112            User = user,
 0113            IsFavorite = !isFavorite.HasValue && isFavoriteInFilters ? true : isFavorite,
 0114            AppearsInItemId = appearsInItemId ?? Guid.Empty,
 0115            ParentId = parentId,
 0116            StartIndex = startIndex,
 0117            Limit = limit ?? 0
 0118        });
 119
 0120        return new QueryResult<BaseItemDto>(
 0121            peopleItems.StartIndex,
 0122            peopleItems.TotalRecordCount,
 0123            peopleItems.Items
 0124                .Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user))
 0125                .ToArray());
 126    }
 127
 128    // People are not owned by a library, so nothing in the Peoples table says which of them a user is
 129    // allowed to see; that only follows from the items they are credited on.
 130    private InternalItemsQuery? BuildAccessFilter(User? user)
 131    {
 0132        if (user is null || !user.HasContentRestrictions())
 133        {
 0134            return null;
 135        }
 136
 0137        var accessFilter = new InternalItemsQuery(user) { IncludeOwnedItems = true };
 0138        _libraryManager.ConfigureUserAccess(accessFilter, user);
 0139        return accessFilter;
 140    }
 141
 142    /// <summary>
 143    /// Get person by name.
 144    /// </summary>
 145    /// <param name="name">Person name.</param>
 146    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 147    /// <response code="200">Person returned.</response>
 148    /// <response code="404">Person not found.</response>
 149    /// <returns>An <see cref="OkResult"/> containing the person on success,
 150    /// or a <see cref="NotFoundResult"/> if person not found.</returns>
 151    [HttpGet("{name}")]
 152    [ProducesResponseType(StatusCodes.Status200OK)]
 153    [ProducesResponseType(StatusCodes.Status404NotFound)]
 154    public ActionResult<BaseItemDto> GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId)
 155    {
 1156        userId = RequestHelpers.GetUserId(User, userId);
 1157        var dtoOptions = new DtoOptions();
 158
 1159        var item = _libraryManager.GetPerson(name);
 1160        if (item is null)
 161        {
 1162            return NotFound();
 163        }
 164
 0165        if (!userId.IsNullOrEmpty())
 166        {
 0167            var user = _userManager.GetUserById(userId.Value);
 0168            return _dtoService.GetBaseItemDto(item, dtoOptions, user);
 169        }
 170
 0171        return _dtoService.GetBaseItemDto(item, dtoOptions);
 172    }
 173}