< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.LibraryController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/LibraryController.cs
Line coverage
23%
Covered lines: 105
Uncovered lines: 334
Coverable lines: 439
Total lines: 1024
Line coverage: 23.9%
Branch coverage
13%
Covered branches: 29
Total branches: 212
Branch coverage: 13.6%
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: 23.5% (97/412) Branch coverage: 14% (27/192) Total lines: 10574/19/2026 - 12:14:27 AM Line coverage: 23.1% (105/453) Branch coverage: 13.6% (29/212) Total lines: 10574/30/2026 - 12:14:58 AM Line coverage: 23.1% (105/453) Branch coverage: 13.6% (29/212) Total lines: 10435/4/2026 - 12:15:16 AM Line coverage: 23.9% (105/439) Branch coverage: 13.6% (29/212) Total lines: 1024 1/23/2026 - 12:11:06 AM Line coverage: 23.5% (97/412) Branch coverage: 14% (27/192) Total lines: 10574/19/2026 - 12:14:27 AM Line coverage: 23.1% (105/453) Branch coverage: 13.6% (29/212) Total lines: 10574/30/2026 - 12:14:58 AM Line coverage: 23.1% (105/453) Branch coverage: 13.6% (29/212) Total lines: 10435/4/2026 - 12:15:16 AM Line coverage: 23.9% (105/439) Branch coverage: 13.6% (29/212) Total lines: 1024

Coverage delta

Coverage delta 1 -1

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetFile(...)50%2275%
GetThemeSongs(...)16.66%1001836.66%
GetThemeVideos(...)16.66%1001836.66%
GetThemeMedia(...)37.5%9871.42%
RefreshLibrary()100%210%
DeleteItem(...)33.33%271252.94%
DeleteItems(...)44.44%461855.55%
GetItemCounts(...)0%620%
GetAncestors(...)20%301041.17%
GetPhysicalPaths()100%210%
GetMediaFolders(...)0%620%
PostUpdatedSeries(...)0%620%
PostUpdatedMovies(...)0%4260%
PostUpdatedMedia(...)0%2040%
GetDownload()10%1232036.36%
GetSimilarItems(...)8.82%5543423.4%
GetLibraryOptionsInfo(...)0%2040%
TranslateParentItem(...)0%620%
LogDownloadAsync()100%210%
GetRepresentativeItemTypes(...)0%210140%
IsSaverEnabledByDefault(...)0%2040%
IsMetadataFetcherEnabledByDefault(...)0%210140%
IsImageFetcherEnabledByDefault(...)0%342180%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel.DataAnnotations;
 4using System.Globalization;
 5using System.IO;
 6using System.Linq;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Jellyfin.Api.Attributes;
 10using Jellyfin.Api.Extensions;
 11using Jellyfin.Api.Helpers;
 12using Jellyfin.Api.ModelBinders;
 13using Jellyfin.Api.Models.LibraryDtos;
 14using Jellyfin.Data.Enums;
 15using Jellyfin.Database.Implementations.Entities;
 16using Jellyfin.Database.Implementations.Enums;
 17using Jellyfin.Extensions;
 18using MediaBrowser.Common.Api;
 19using MediaBrowser.Common.Extensions;
 20using MediaBrowser.Controller.Configuration;
 21using MediaBrowser.Controller.Dto;
 22using MediaBrowser.Controller.Entities;
 23using MediaBrowser.Controller.Entities.Audio;
 24using MediaBrowser.Controller.Entities.Movies;
 25using MediaBrowser.Controller.Entities.TV;
 26using MediaBrowser.Controller.IO;
 27using MediaBrowser.Controller.Library;
 28using MediaBrowser.Controller.Providers;
 29using MediaBrowser.Model.Activity;
 30using MediaBrowser.Model.Configuration;
 31using MediaBrowser.Model.Dto;
 32using MediaBrowser.Model.Entities;
 33using MediaBrowser.Model.Globalization;
 34using MediaBrowser.Model.Net;
 35using MediaBrowser.Model.Querying;
 36using Microsoft.AspNetCore.Authorization;
 37using Microsoft.AspNetCore.Http;
 38using Microsoft.AspNetCore.Mvc;
 39using Microsoft.Extensions.Logging;
 40
 41namespace Jellyfin.Api.Controllers;
 42
 43/// <summary>
 44/// Library Controller.
 45/// </summary>
 46[Route("")]
 47public class LibraryController : BaseJellyfinApiController
 48{
 49    private readonly IProviderManager _providerManager;
 50    private readonly ILibraryManager _libraryManager;
 51    private readonly IUserManager _userManager;
 52    private readonly IDtoService _dtoService;
 53    private readonly IActivityManager _activityManager;
 54    private readonly ILocalizationManager _localization;
 55    private readonly ILibraryMonitor _libraryMonitor;
 56    private readonly ILogger<LibraryController> _logger;
 57    private readonly IServerConfigurationManager _serverConfigurationManager;
 58
 59    /// <summary>
 60    /// Initializes a new instance of the <see cref="LibraryController"/> class.
 61    /// </summary>
 62    /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
 63    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 64    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 65    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 66    /// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param>
 67    /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 68    /// <param name="libraryMonitor">Instance of the <see cref="ILibraryMonitor"/> interface.</param>
 69    /// <param name="logger">Instance of the <see cref="ILogger{LibraryController}"/> interface.</param>
 70    /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</p
 1471    public LibraryController(
 1472        IProviderManager providerManager,
 1473        ILibraryManager libraryManager,
 1474        IUserManager userManager,
 1475        IDtoService dtoService,
 1476        IActivityManager activityManager,
 1477        ILocalizationManager localization,
 1478        ILibraryMonitor libraryMonitor,
 1479        ILogger<LibraryController> logger,
 1480        IServerConfigurationManager serverConfigurationManager)
 81    {
 1482        _providerManager = providerManager;
 1483        _libraryManager = libraryManager;
 1484        _userManager = userManager;
 1485        _dtoService = dtoService;
 1486        _activityManager = activityManager;
 1487        _localization = localization;
 1488        _libraryMonitor = libraryMonitor;
 1489        _logger = logger;
 1490        _serverConfigurationManager = serverConfigurationManager;
 1491    }
 92
 93    /// <summary>
 94    /// Get the original file of an item.
 95    /// </summary>
 96    /// <param name="itemId">The item id.</param>
 97    /// <response code="200">File stream returned.</response>
 98    /// <response code="404">Item not found.</response>
 99    /// <returns>A <see cref="FileStreamResult"/> with the original file.</returns>
 100    [HttpGet("Items/{itemId}/File")]
 101    [Authorize]
 102    [ProducesResponseType(StatusCodes.Status200OK)]
 103    [ProducesResponseType(StatusCodes.Status404NotFound)]
 104    [ProducesFile("video/*", "audio/*")]
 105    public ActionResult GetFile([FromRoute, Required] Guid itemId)
 106    {
 1107        var item = _libraryManager.GetItemById<BaseItem>(itemId, User.GetUserId());
 1108        if (item is null)
 109        {
 1110            return NotFound();
 111        }
 112
 0113        return PhysicalFile(item.Path, MimeTypes.GetMimeType(item.Path), true);
 114    }
 115
 116    /// <summary>
 117    /// Get theme songs for an item.
 118    /// </summary>
 119    /// <param name="itemId">The item id.</param>
 120    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 121    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 122    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 123    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 124    /// <response code="200">Theme songs returned.</response>
 125    /// <response code="404">Item not found.</response>
 126    /// <returns>The item theme songs.</returns>
 127    [HttpGet("Items/{itemId}/ThemeSongs")]
 128    [Authorize]
 129    [ProducesResponseType(StatusCodes.Status200OK)]
 130    [ProducesResponseType(StatusCodes.Status404NotFound)]
 131    public ActionResult<ThemeMediaResult> GetThemeSongs(
 132        [FromRoute, Required] Guid itemId,
 133        [FromQuery] Guid? userId,
 134        [FromQuery] bool inheritFromParent = false,
 135        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 136        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 137    {
 2138        userId = RequestHelpers.GetUserId(User, userId);
 2139        var user = userId.IsNullOrEmpty()
 2140            ? null
 2141            : _userManager.GetUserById(userId.Value);
 142
 2143        var item = itemId.IsEmpty()
 2144            ? (userId.IsNullOrEmpty()
 2145                ? _libraryManager.RootFolder
 2146                : _libraryManager.GetUserRootFolder())
 2147            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 2148        if (item is null)
 149        {
 2150            return NotFound();
 151        }
 152
 0153        sortOrder ??= [];
 0154        sortBy ??= [];
 0155        var orderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder);
 156
 157        IReadOnlyList<BaseItem> themeItems;
 158
 0159        while (true)
 160        {
 0161            themeItems = item.GetThemeSongs(user, orderBy);
 162
 0163            if (themeItems.Count > 0 || !inheritFromParent)
 164            {
 165                break;
 166            }
 167
 0168            var parent = item.GetParent();
 0169            if (parent is null)
 170            {
 171                break;
 172            }
 173
 0174            item = parent;
 175        }
 176
 0177        var dtoOptions = new DtoOptions();
 0178        var items = themeItems
 0179            .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
 0180            .ToArray();
 181
 0182        return new ThemeMediaResult
 0183        {
 0184            Items = items,
 0185            TotalRecordCount = items.Length,
 0186            OwnerId = item.Id
 0187        };
 188    }
 189
 190    /// <summary>
 191    /// Get theme videos for an item.
 192    /// </summary>
 193    /// <param name="itemId">The item id.</param>
 194    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 195    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 196    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 197    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 198    /// <response code="200">Theme videos returned.</response>
 199    /// <response code="404">Item not found.</response>
 200    /// <returns>The item theme videos.</returns>
 201    [HttpGet("Items/{itemId}/ThemeVideos")]
 202    [Authorize]
 203    [ProducesResponseType(StatusCodes.Status200OK)]
 204    [ProducesResponseType(StatusCodes.Status404NotFound)]
 205    public ActionResult<ThemeMediaResult> GetThemeVideos(
 206        [FromRoute, Required] Guid itemId,
 207        [FromQuery] Guid? userId,
 208        [FromQuery] bool inheritFromParent = false,
 209        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 210        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 211    {
 2212        userId = RequestHelpers.GetUserId(User, userId);
 2213        var user = userId.IsNullOrEmpty()
 2214            ? null
 2215            : _userManager.GetUserById(userId.Value);
 2216        var item = itemId.IsEmpty()
 2217            ? (userId.IsNullOrEmpty()
 2218                ? _libraryManager.RootFolder
 2219                : _libraryManager.GetUserRootFolder())
 2220            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 2221        if (item is null)
 222        {
 2223            return NotFound();
 224        }
 225
 0226        sortOrder ??= [];
 0227        sortBy ??= [];
 0228        var orderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder);
 229
 230        IEnumerable<BaseItem> themeItems;
 231
 0232        while (true)
 233        {
 0234            themeItems = item.GetThemeVideos(user, orderBy);
 235
 0236            if (themeItems.Any() || !inheritFromParent)
 237            {
 238                break;
 239            }
 240
 0241            var parent = item.GetParent();
 0242            if (parent is null)
 243            {
 244                break;
 245            }
 246
 0247            item = parent;
 248        }
 249
 0250        var dtoOptions = new DtoOptions();
 0251        var items = themeItems
 0252            .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
 0253            .ToArray();
 254
 0255        return new ThemeMediaResult
 0256        {
 0257            Items = items,
 0258            TotalRecordCount = items.Length,
 0259            OwnerId = item.Id
 0260        };
 261    }
 262
 263    /// <summary>
 264    /// Get theme songs and videos for an item.
 265    /// </summary>
 266    /// <param name="itemId">The item id.</param>
 267    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 268    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 269    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 270    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 271    /// <response code="200">Theme songs and videos returned.</response>
 272    /// <response code="404">Item not found.</response>
 273    /// <returns>The item theme videos.</returns>
 274    [HttpGet("Items/{itemId}/ThemeMedia")]
 275    [Authorize]
 276    [ProducesResponseType(StatusCodes.Status200OK)]
 277    public ActionResult<AllThemeMediaResult> GetThemeMedia(
 278        [FromRoute, Required] Guid itemId,
 279        [FromQuery] Guid? userId,
 280        [FromQuery] bool inheritFromParent = false,
 281        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 282        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 283    {
 1284        var themeSongs = GetThemeSongs(
 1285            itemId,
 1286            userId,
 1287            inheritFromParent,
 1288            sortBy,
 1289            sortOrder);
 290
 1291        var themeVideos = GetThemeVideos(
 1292            itemId,
 1293            userId,
 1294            inheritFromParent,
 1295            sortBy,
 1296            sortOrder);
 297
 1298        if (themeSongs.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound }
 1299            || themeVideos.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound })
 300        {
 1301            return NotFound();
 302        }
 303
 0304        return new AllThemeMediaResult
 0305        {
 0306            ThemeSongsResult = themeSongs.Value,
 0307            ThemeVideosResult = themeVideos.Value,
 0308            SoundtrackSongsResult = new ThemeMediaResult()
 0309        };
 310    }
 311
 312    /// <summary>
 313    /// Starts a library scan.
 314    /// </summary>
 315    /// <response code="204">Library scan started.</response>
 316    /// <returns>A <see cref="NoContentResult"/>.</returns>
 317    [HttpPost("Library/Refresh")]
 318    [Authorize(Policy = Policies.RequiresElevation)]
 319    [ProducesResponseType(StatusCodes.Status204NoContent)]
 320    public async Task<ActionResult> RefreshLibrary()
 321    {
 322        try
 323        {
 0324            await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(fa
 0325        }
 0326        catch (Exception ex)
 327        {
 0328            _logger.LogError(ex, "Error refreshing library");
 0329        }
 330
 0331        return NoContent();
 0332    }
 333
 334    /// <summary>
 335    /// Deletes an item from the library and filesystem.
 336    /// </summary>
 337    /// <param name="itemId">The item id.</param>
 338    /// <response code="204">Item deleted.</response>
 339    /// <response code="401">Unauthorized access.</response>
 340    /// <response code="404">Item not found.</response>
 341    /// <returns>A <see cref="NoContentResult"/>.</returns>
 342    [HttpDelete("Items/{itemId}")]
 343    [Authorize]
 344    [ProducesResponseType(StatusCodes.Status204NoContent)]
 345    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 346    [ProducesResponseType(StatusCodes.Status404NotFound)]
 347    public ActionResult DeleteItem(Guid itemId)
 348    {
 1349        var userId = User.GetUserId();
 1350        var isApiKey = User.GetIsApiKey();
 1351        var user = userId.IsEmpty() && isApiKey
 1352            ? null
 1353            : _userManager.GetUserById(userId);
 354
 1355        if (user is null && !isApiKey)
 356        {
 0357            return NotFound();
 358        }
 359
 1360        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1361        if (item is null)
 362        {
 1363            return NotFound();
 364        }
 365
 0366        if (user is not null && !item.CanDelete(user))
 367        {
 0368            return Unauthorized("Unauthorized access");
 369        }
 370
 0371        _libraryManager.DeleteItem(
 0372            item,
 0373            new DeleteOptions { DeleteFileLocation = true },
 0374            true);
 375
 0376        return NoContent();
 377    }
 378
 379    /// <summary>
 380    /// Deletes items from the library and filesystem.
 381    /// </summary>
 382    /// <param name="ids">The item ids.</param>
 383    /// <response code="204">Items deleted.</response>
 384    /// <response code="401">Unauthorized access.</response>
 385    /// <returns>A <see cref="NoContentResult"/>.</returns>
 386    [HttpDelete("Items")]
 387    [Authorize]
 388    [ProducesResponseType(StatusCodes.Status204NoContent)]
 389    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 390    [ProducesResponseType(StatusCodes.Status404NotFound)]
 391    public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids)
 392    {
 1393        var isApiKey = User.GetIsApiKey();
 1394        var userId = User.GetUserId();
 1395        var user = !isApiKey && !userId.IsEmpty()
 1396            ? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException()
 1397            : null;
 398
 1399        if (!isApiKey && user is null)
 400        {
 0401            return Unauthorized("Unauthorized access");
 402        }
 403
 3404        foreach (var i in ids)
 405        {
 1406            var item = _libraryManager.GetItemById<BaseItem>(i, user);
 1407            if (item is null)
 408            {
 1409                return NotFound();
 410            }
 411
 0412            if (user is not null && !item.CanDelete(user))
 413            {
 0414                return Unauthorized("Unauthorized access");
 415            }
 416
 0417            _libraryManager.DeleteItem(
 0418                item,
 0419                new DeleteOptions { DeleteFileLocation = true },
 0420                true);
 421        }
 422
 0423        return NoContent();
 424    }
 425
 426    /// <summary>
 427    /// Get item counts.
 428    /// </summary>
 429    /// <param name="userId">Optional. Get counts from a specific user's library.</param>
 430    /// <param name="isFavorite">Optional. Get counts of favorite items.</param>
 431    /// <response code="200">Item counts returned.</response>
 432    /// <returns>Item counts.</returns>
 433    [HttpGet("Items/Counts")]
 434    [Authorize]
 435    [ProducesResponseType(StatusCodes.Status200OK)]
 436    public ActionResult<ItemCounts> GetItemCounts(
 437        [FromQuery] Guid? userId,
 438        [FromQuery] bool? isFavorite)
 439    {
 0440        userId = RequestHelpers.GetUserId(User, userId);
 0441        var user = userId.IsNullOrEmpty()
 0442            ? null
 0443            : _userManager.GetUserById(userId.Value);
 444
 0445        var query = new InternalItemsQuery(user)
 0446        {
 0447            Recursive = true,
 0448            IsVirtualItem = false,
 0449            IsFavorite = isFavorite,
 0450            DtoOptions = new DtoOptions(false)
 0451            {
 0452                EnableImages = false
 0453            }
 0454        };
 455
 0456        return _libraryManager.GetItemCounts(query);
 457    }
 458
 459    /// <summary>
 460    /// Gets all parents of an item.
 461    /// </summary>
 462    /// <param name="itemId">The item id.</param>
 463    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 464    /// <response code="200">Item parents returned.</response>
 465    /// <response code="404">Item not found.</response>
 466    /// <returns>Item parents.</returns>
 467    [HttpGet("Items/{itemId}/Ancestors")]
 468    [Authorize]
 469    [ProducesResponseType(StatusCodes.Status200OK)]
 470    [ProducesResponseType(StatusCodes.Status404NotFound)]
 471    public ActionResult<IEnumerable<BaseItemDto>> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? user
 472    {
 1473        userId = RequestHelpers.GetUserId(User, userId);
 1474        var user = userId.IsNullOrEmpty()
 1475            ? null
 1476            : _userManager.GetUserById(userId.Value);
 1477        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1478        if (item is null)
 479        {
 1480            return NotFound();
 481        }
 482
 0483        var baseItemDtos = new List<BaseItemDto>();
 484
 0485        var dtoOptions = new DtoOptions();
 0486        BaseItem? parent = item.GetParent();
 487
 0488        while (parent is not null)
 489        {
 0490            if (user is not null)
 491            {
 0492                parent = TranslateParentItem(parent, user);
 0493                if (parent is null)
 494                {
 495                    break;
 496                }
 497            }
 498
 0499            baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
 500
 0501            parent = parent.GetParent();
 502        }
 503
 0504        return baseItemDtos;
 505    }
 506
 507    /// <summary>
 508    /// Gets a list of physical paths from virtual folders.
 509    /// </summary>
 510    /// <response code="200">Physical paths returned.</response>
 511    /// <returns>List of physical paths.</returns>
 512    [HttpGet("Library/PhysicalPaths")]
 513    [Authorize(Policy = Policies.RequiresElevation)]
 514    [ProducesResponseType(StatusCodes.Status200OK)]
 515    public ActionResult<IEnumerable<string>> GetPhysicalPaths()
 516    {
 0517        return Ok(_libraryManager.RootFolder.Children
 0518            .SelectMany(c => c.PhysicalLocations));
 519    }
 520
 521    /// <summary>
 522    /// Gets all user media folders.
 523    /// </summary>
 524    /// <param name="isHidden">Optional. Filter by folders that are marked hidden, or not.</param>
 525    /// <response code="200">Media folders returned.</response>
 526    /// <returns>List of user media folders.</returns>
 527    [HttpGet("Library/MediaFolders")]
 528    [Authorize(Policy = Policies.RequiresElevation)]
 529    [ProducesResponseType(StatusCodes.Status200OK)]
 530    public ActionResult<QueryResult<BaseItemDto>> GetMediaFolders([FromQuery] bool? isHidden)
 531    {
 0532        var items = _libraryManager.GetUserRootFolder().Children
 0533            .Concat(_libraryManager.RootFolder.VirtualChildren)
 0534            .Where(i => _libraryManager.GetLibraryOptions(i).Enabled)
 0535            .OrderBy(i => i.SortName)
 0536            .ToList();
 537
 0538        if (isHidden.HasValue)
 539        {
 0540            var val = isHidden.Value;
 541
 0542            items = items.Where(i => i.IsHidden == val).ToList();
 543        }
 544
 0545        var dtoOptions = new DtoOptions();
 0546        var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions);
 0547        return new QueryResult<BaseItemDto>(resultArray);
 548    }
 549
 550    /// <summary>
 551    /// Reports that new episodes of a series have been added by an external source.
 552    /// </summary>
 553    /// <param name="tvdbId">The tvdbId.</param>
 554    /// <response code="204">Report success.</response>
 555    /// <returns>A <see cref="NoContentResult"/>.</returns>
 556    [HttpPost("Library/Series/Added", Name = "PostAddedSeries")]
 557    [HttpPost("Library/Series/Updated")]
 558    [Authorize]
 559    [ProducesResponseType(StatusCodes.Status204NoContent)]
 560    public ActionResult PostUpdatedSeries([FromQuery] string? tvdbId)
 561    {
 0562        var series = _libraryManager.GetItemList(new InternalItemsQuery
 0563        {
 0564            IncludeItemTypes = new[] { BaseItemKind.Series },
 0565            DtoOptions = new DtoOptions(false)
 0566            {
 0567                EnableImages = false
 0568            }
 0569        }).Where(i => string.Equals(tvdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Tvdb), StringCo
 570
 0571        foreach (var item in series)
 572        {
 0573            _libraryMonitor.ReportFileSystemChanged(item.Path);
 574        }
 575
 0576        return NoContent();
 577    }
 578
 579    /// <summary>
 580    /// Reports that new movies have been added by an external source.
 581    /// </summary>
 582    /// <param name="tmdbId">The tmdbId.</param>
 583    /// <param name="imdbId">The imdbId.</param>
 584    /// <response code="204">Report success.</response>
 585    /// <returns>A <see cref="NoContentResult"/>.</returns>
 586    [HttpPost("Library/Movies/Added", Name = "PostAddedMovies")]
 587    [HttpPost("Library/Movies/Updated")]
 588    [Authorize]
 589    [ProducesResponseType(StatusCodes.Status204NoContent)]
 590    public ActionResult PostUpdatedMovies([FromQuery] string? tmdbId, [FromQuery] string? imdbId)
 591    {
 0592        var movies = _libraryManager.GetItemList(new InternalItemsQuery
 0593        {
 0594            IncludeItemTypes = new[] { BaseItemKind.Movie },
 0595            DtoOptions = new DtoOptions(false)
 0596            {
 0597                EnableImages = false
 0598            }
 0599        });
 600
 0601        if (!string.IsNullOrWhiteSpace(imdbId))
 602        {
 0603            movies = movies.Where(i => string.Equals(imdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvide
 604        }
 0605        else if (!string.IsNullOrWhiteSpace(tmdbId))
 606        {
 0607            movies = movies.Where(i => string.Equals(tmdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvide
 608        }
 609        else
 610        {
 0611            movies = new List<BaseItem>();
 612        }
 613
 0614        foreach (var item in movies)
 615        {
 0616            _libraryMonitor.ReportFileSystemChanged(item.Path);
 617        }
 618
 0619        return NoContent();
 620    }
 621
 622    /// <summary>
 623    /// Reports that new movies have been added by an external source.
 624    /// </summary>
 625    /// <param name="dto">The update paths.</param>
 626    /// <response code="204">Report success.</response>
 627    /// <returns>A <see cref="NoContentResult"/>.</returns>
 628    [HttpPost("Library/Media/Updated")]
 629    [Authorize]
 630    [ProducesResponseType(StatusCodes.Status204NoContent)]
 631    public ActionResult PostUpdatedMedia([FromBody, Required] MediaUpdateInfoDto dto)
 632    {
 0633        foreach (var item in dto.Updates)
 634        {
 0635            _libraryMonitor.ReportFileSystemChanged(item.Path ?? throw new ArgumentException("Item path can't be null.")
 636        }
 637
 0638        return NoContent();
 639    }
 640
 641    /// <summary>
 642    /// Downloads item media.
 643    /// </summary>
 644    /// <param name="itemId">The item id.</param>
 645    /// <response code="200">Media downloaded.</response>
 646    /// <response code="404">Item not found.</response>
 647    /// <returns>A <see cref="FileResult"/> containing the media stream.</returns>
 648    /// <exception cref="ArgumentException">User can't download or item can't be downloaded.</exception>
 649    [HttpGet("Items/{itemId}/Download")]
 650    [Authorize(Policy = Policies.Download)]
 651    [ProducesResponseType(StatusCodes.Status200OK)]
 652    [ProducesResponseType(StatusCodes.Status404NotFound)]
 653    [ProducesFile("video/*", "audio/*")]
 654    public async Task<ActionResult> GetDownload([FromRoute, Required] Guid itemId)
 655    {
 1656        var userId = User.GetUserId();
 1657        var user = userId.IsEmpty()
 1658            ? null
 1659            : _userManager.GetUserById(userId);
 1660        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1661        if (item is null)
 662        {
 1663            return NotFound();
 664        }
 665
 0666        if (user is not null)
 667        {
 0668            if (!item.CanDownload(user))
 669            {
 0670                throw new ArgumentException("Item does not support downloading");
 671            }
 672        }
 673        else
 674        {
 0675            if (!item.CanDownload())
 676            {
 0677                throw new ArgumentException("Item does not support downloading");
 678            }
 679        }
 680
 0681        if (user is not null)
 682        {
 0683            await LogDownloadAsync(item, user).ConfigureAwait(false);
 684        }
 685
 686        // Quotes are valid in linux. They'll possibly cause issues here.
 0687        var filename = Path.GetFileName(item.Path)?.Replace("\"", string.Empty, StringComparison.Ordinal);
 688
 0689        var filePath = item.Path;
 0690        if (item.IsFileProtocol)
 691        {
 692            // PhysicalFile does not work well with symlinks at the moment.
 0693            var resolved = FileSystemHelper.ResolveLinkTarget(filePath, returnFinalTarget: true);
 0694            if (resolved is not null && resolved.Exists)
 695            {
 0696                filePath = resolved.FullName;
 697            }
 698        }
 699
 0700        return PhysicalFile(filePath, MimeTypes.GetMimeType(filePath), filename, true);
 1701    }
 702
 703    /// <summary>
 704    /// Gets similar items.
 705    /// </summary>
 706    /// <param name="itemId">The item id.</param>
 707    /// <param name="excludeArtistIds">Exclude artist ids.</param>
 708    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 709    /// <param name="limit">Optional. The maximum number of records to return.</param>
 710    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 711    /// <response code="200">Similar items returned.</response>
 712    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> containing the similar items.</returns>
 713    [HttpGet("Artists/{itemId}/Similar", Name = "GetSimilarArtists")]
 714    [HttpGet("Items/{itemId}/Similar")]
 715    [HttpGet("Albums/{itemId}/Similar", Name = "GetSimilarAlbums")]
 716    [HttpGet("Shows/{itemId}/Similar", Name = "GetSimilarShows")]
 717    [HttpGet("Movies/{itemId}/Similar", Name = "GetSimilarMovies")]
 718    [HttpGet("Trailers/{itemId}/Similar", Name = "GetSimilarTrailers")]
 719    [Authorize]
 720    [ProducesResponseType(StatusCodes.Status200OK)]
 721    public ActionResult<QueryResult<BaseItemDto>> GetSimilarItems(
 722        [FromRoute, Required] Guid itemId,
 723        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 724        [FromQuery] Guid? userId,
 725        [FromQuery] int? limit,
 726        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields)
 727    {
 6728        userId = RequestHelpers.GetUserId(User, userId);
 6729        var user = userId.IsNullOrEmpty()
 6730            ? null
 6731            : _userManager.GetUserById(userId.Value);
 6732        var item = itemId.IsEmpty()
 6733            ? (user is null
 6734                ? _libraryManager.RootFolder
 6735                : _libraryManager.GetUserRootFolder())
 6736            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 6737        if (item is null)
 738        {
 6739            return NotFound();
 740        }
 741
 0742        if (item is Episode || (item is IItemByName && item is not MusicArtist))
 743        {
 0744            return new QueryResult<BaseItemDto>();
 745        }
 746
 0747        var dtoOptions = new DtoOptions { Fields = fields };
 748
 0749        var program = item as IHasProgramAttributes;
 0750        bool? isMovie = item is Movie || (program is not null && program.IsMovie) || item is Trailer;
 0751        bool? isSeries = item is Series || (program is not null && program.IsSeries);
 752
 0753        var includeItemTypes = new List<BaseItemKind>();
 0754        if (isMovie.Value)
 755        {
 0756            includeItemTypes.Add(BaseItemKind.Movie);
 0757            if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
 758            {
 0759                includeItemTypes.Add(BaseItemKind.Trailer);
 0760                includeItemTypes.Add(BaseItemKind.LiveTvProgram);
 761            }
 762        }
 0763        else if (isSeries.Value)
 764        {
 0765            includeItemTypes.Add(BaseItemKind.Series);
 766        }
 767        else
 768        {
 769            // For non series and movie types these columns are typically null
 770            // isSeries = null;
 0771            isMovie = null;
 0772            includeItemTypes.Add(item.GetBaseItemKind());
 773        }
 774
 0775        var query = new InternalItemsQuery(user)
 0776        {
 0777            Genres = item.Genres,
 0778            Tags = item.Tags,
 0779            Limit = limit,
 0780            IncludeItemTypes = includeItemTypes.ToArray(),
 0781            DtoOptions = dtoOptions,
 0782            EnableTotalRecordCount = !isMovie ?? true,
 0783            EnableGroupByMetadataKey = isMovie ?? false,
 0784            ExcludeItemIds = [itemId],
 0785            OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)]
 0786        };
 787
 788        // ExcludeArtistIds
 0789        if (excludeArtistIds.Length != 0)
 790        {
 0791            query.ExcludeArtistIds = excludeArtistIds;
 792        }
 793
 0794        var itemsResult = _libraryManager.GetItemList(query);
 795
 0796        var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
 797
 0798        return new QueryResult<BaseItemDto>(
 0799            query.StartIndex,
 0800            itemsResult.Count,
 0801            returnList);
 802    }
 803
 804    /// <summary>
 805    /// Gets the library options info.
 806    /// </summary>
 807    /// <param name="libraryContentType">Library content type.</param>
 808    /// <param name="isNewLibrary">Whether this is a new library.</param>
 809    /// <response code="200">Library options info returned.</response>
 810    /// <returns>Library options info.</returns>
 811    [HttpGet("Libraries/AvailableOptions")]
 812    [Authorize(Policy = Policies.FirstTimeSetupOrDefault)]
 813    [ProducesResponseType(StatusCodes.Status200OK)]
 814    public ActionResult<LibraryOptionsResultDto> GetLibraryOptionsInfo(
 815        [FromQuery] CollectionType? libraryContentType,
 816        [FromQuery] bool isNewLibrary = false)
 817    {
 0818        var result = new LibraryOptionsResultDto();
 819
 0820        var types = GetRepresentativeItemTypes(libraryContentType);
 0821        var typesList = types.ToList();
 822
 0823        var plugins = _providerManager.GetAllMetadataPlugins()
 0824            .Where(i => types.Contains(i.ItemType, StringComparison.OrdinalIgnoreCase))
 0825            .OrderBy(i => typesList.IndexOf(i.ItemType))
 0826            .ToList();
 827
 0828        result.MetadataSavers = plugins
 0829            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataSaver))
 0830            .Select(i => new LibraryOptionInfoDto
 0831            {
 0832                Name = i.Name,
 0833                DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary)
 0834            })
 0835            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0836            .ToArray();
 837
 0838        result.MetadataReaders = plugins
 0839            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LocalMetadataProvider))
 0840            .Select(i => new LibraryOptionInfoDto
 0841            {
 0842                Name = i.Name,
 0843                DefaultEnabled = true
 0844            })
 0845            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0846            .ToArray();
 847
 0848        result.SubtitleFetchers = plugins
 0849            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.SubtitleFetcher))
 0850            .Select(i => new LibraryOptionInfoDto
 0851            {
 0852                Name = i.Name,
 0853                DefaultEnabled = true
 0854            })
 0855            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0856            .ToArray();
 857
 0858        result.LyricFetchers = plugins
 0859            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LyricFetcher))
 0860            .Select(i => new LibraryOptionInfoDto
 0861            {
 0862                Name = i.Name,
 0863                DefaultEnabled = true
 0864            })
 0865            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0866            .ToArray();
 867
 0868        result.MediaSegmentProviders = plugins
 0869            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MediaSegmentProvider))
 0870            .Select(i => new LibraryOptionInfoDto
 0871            {
 0872                Name = i.Name,
 0873                DefaultEnabled = true
 0874            })
 0875            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0876            .ToArray();
 877
 0878        var typeOptions = new List<LibraryTypeOptionsDto>();
 879
 0880        foreach (var type in types)
 881        {
 0882            TypeOptions.DefaultImageOptions.TryGetValue(type, out var defaultImageOptions);
 883
 0884            typeOptions.Add(new LibraryTypeOptionsDto
 0885            {
 0886                Type = type,
 0887
 0888                MetadataFetchers = plugins
 0889                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0890                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher))
 0891                    .Select(i => new LibraryOptionInfoDto
 0892                    {
 0893                        Name = i.Name,
 0894                        DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
 0895                    })
 0896                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0897                    .ToArray(),
 0898
 0899                ImageFetchers = plugins
 0900                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0901                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher))
 0902                    .Select(i => new LibraryOptionInfoDto
 0903                    {
 0904                        Name = i.Name,
 0905                        DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
 0906                    })
 0907                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0908                    .ToArray(),
 0909
 0910                SupportedImageTypes = plugins
 0911                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0912                    .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>())
 0913                    .Distinct()
 0914                    .ToArray(),
 0915
 0916                DefaultImageOptions = defaultImageOptions ?? Array.Empty<ImageOption>()
 0917            });
 918        }
 919
 0920        result.TypeOptions = typeOptions.ToArray();
 921
 0922        return result;
 923    }
 924
 925    private BaseItem? TranslateParentItem(BaseItem item, User user)
 926    {
 0927        return item.GetParent() is AggregateFolder
 0928            ? _libraryManager.GetUserRootFolder().GetChildren(user, true)
 0929                .FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path))
 0930            : item;
 931    }
 932
 933    private async Task LogDownloadAsync(BaseItem item, User user)
 934    {
 935        try
 936        {
 0937            await _activityManager.CreateAsync(new ActivityLog(
 0938                string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithVal
 0939                "UserDownloadingContent",
 0940                User.GetUserId())
 0941            {
 0942                ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceV
 0943                ItemId = item.Id.ToString("N", CultureInfo.InvariantCulture)
 0944            }).ConfigureAwait(false);
 0945        }
 0946        catch
 947        {
 948            // Logged at lower levels
 0949        }
 0950    }
 951
 952    private static string[] GetRepresentativeItemTypes(CollectionType? contentType)
 953    {
 0954        return contentType switch
 0955        {
 0956            CollectionType.boxsets => new[] { "BoxSet" },
 0957            CollectionType.playlists => new[] { "Playlist" },
 0958            CollectionType.movies => new[] { "Movie" },
 0959            CollectionType.tvshows => new[] { "Series", "Season", "Episode" },
 0960            CollectionType.books => new[] { "Book", "AudioBook" },
 0961            CollectionType.music => new[] { "MusicArtist", "MusicAlbum", "Audio", "MusicVideo" },
 0962            CollectionType.homevideos => new[] { "Video", "Photo" },
 0963            CollectionType.photos => new[] { "Video", "Photo" },
 0964            CollectionType.musicvideos => new[] { "MusicVideo" },
 0965            _ => new[] { "Series", "Season", "Episode", "Movie" }
 0966        };
 967    }
 968
 969    private bool IsSaverEnabledByDefault(string name, string[] itemTypes, bool isNewLibrary)
 970    {
 0971        if (isNewLibrary)
 972        {
 0973            return false;
 974        }
 975
 0976        var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
 0977            .Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
 0978            .ToArray();
 979
 0980        return metadataOptions.Length == 0 || metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringCo
 981    }
 982
 983    private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
 984    {
 0985        if (isNewLibrary)
 986        {
 0987            if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
 988            {
 0989                return !(string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
 0990                         || string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
 0991                         || string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase));
 992            }
 993
 0994            return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
 0995                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
 0996                   || string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase);
 997        }
 998
 0999        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
 01000        return metadataOptions is null || !metadataOptions.DisabledMetadataFetchers.Contains(name, StringComparison.Ordi
 1001    }
 1002
 1003    private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
 1004    {
 01005        if (isNewLibrary)
 1006        {
 01007            if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
 1008            {
 01009                return !string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase)
 01010                       && !string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
 01011                       && !string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
 01012                       && !string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase);
 1013            }
 1014
 01015            return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
 01016                   || string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase)
 01017                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
 01018                   || string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase);
 1019        }
 1020
 01021        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
 01022        return metadataOptions is null || !metadataOptions.DisabledImageFetchers.Contains(name, StringComparison.Ordinal
 1023    }
 1024}

Methods/Properties

.ctor(MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Dto.IDtoService,MediaBrowser.Model.Activity.IActivityManager,MediaBrowser.Model.Globalization.ILocalizationManager,MediaBrowser.Controller.Library.ILibraryMonitor,Microsoft.Extensions.Logging.ILogger`1<Jellyfin.Api.Controllers.LibraryController>,MediaBrowser.Controller.Configuration.IServerConfigurationManager)
GetFile(System.Guid)
GetThemeSongs(System.Guid,System.Nullable`1<System.Guid>,System.Boolean,Jellyfin.Data.Enums.ItemSortBy[],Jellyfin.Database.Implementations.Enums.SortOrder[])
GetThemeVideos(System.Guid,System.Nullable`1<System.Guid>,System.Boolean,Jellyfin.Data.Enums.ItemSortBy[],Jellyfin.Database.Implementations.Enums.SortOrder[])
GetThemeMedia(System.Guid,System.Nullable`1<System.Guid>,System.Boolean,Jellyfin.Data.Enums.ItemSortBy[],Jellyfin.Database.Implementations.Enums.SortOrder[])
RefreshLibrary()
DeleteItem(System.Guid)
DeleteItems(System.Guid[])
GetItemCounts(System.Nullable`1<System.Guid>,System.Nullable`1<System.Boolean>)
GetAncestors(System.Guid,System.Nullable`1<System.Guid>)
GetPhysicalPaths()
GetMediaFolders(System.Nullable`1<System.Boolean>)
PostUpdatedSeries(System.String)
PostUpdatedMovies(System.String,System.String)
PostUpdatedMedia(Jellyfin.Api.Models.LibraryDtos.MediaUpdateInfoDto)
GetDownload()
GetSimilarItems(System.Guid,System.Guid[],System.Nullable`1<System.Guid>,System.Nullable`1<System.Int32>,MediaBrowser.Model.Querying.ItemFields[])
GetLibraryOptionsInfo(System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,System.Boolean)
TranslateParentItem(MediaBrowser.Controller.Entities.BaseItem,Jellyfin.Database.Implementations.Entities.User)
LogDownloadAsync()
GetRepresentativeItemTypes(System.Nullable`1<Jellyfin.Data.Enums.CollectionType>)
IsSaverEnabledByDefault(System.String,System.String[],System.Boolean)
IsMetadataFetcherEnabledByDefault(System.String,System.String,System.Boolean)
IsImageFetcherEnabledByDefault(System.String,System.String,System.Boolean)