< 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: 97
Uncovered lines: 313
Coverable lines: 410
Total lines: 1043
Line coverage: 23.6%
Branch coverage
14%
Covered branches: 27
Total branches: 192
Branch coverage: 14%
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%11100%
GetFile(...)50%2275%
GetThemeSongs(...)16.66%1001836.66%
GetThemeVideos(...)16.66%1001836.66%
GetThemeMedia(...)37.5%9871.42%
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%
GetSimilarItems(...)8.82%5333424.44%
GetLibraryOptionsInfo(...)0%2040%
GetCount(...)100%210%
TranslateParentItem(...)0%620%
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.Library;
 27using MediaBrowser.Controller.Providers;
 28using MediaBrowser.Model.Activity;
 29using MediaBrowser.Model.Configuration;
 30using MediaBrowser.Model.Dto;
 31using MediaBrowser.Model.Entities;
 32using MediaBrowser.Model.Globalization;
 33using MediaBrowser.Model.Net;
 34using MediaBrowser.Model.Querying;
 35using Microsoft.AspNetCore.Authorization;
 36using Microsoft.AspNetCore.Http;
 37using Microsoft.AspNetCore.Mvc;
 38using Microsoft.Extensions.Logging;
 39
 40namespace Jellyfin.Api.Controllers;
 41
 42/// <summary>
 43/// Library Controller.
 44/// </summary>
 45[Route("")]
 46public class LibraryController : BaseJellyfinApiController
 47{
 48    private readonly IProviderManager _providerManager;
 49    private readonly ILibraryManager _libraryManager;
 50    private readonly IUserManager _userManager;
 51    private readonly IDtoService _dtoService;
 52    private readonly IActivityManager _activityManager;
 53    private readonly ILocalizationManager _localization;
 54    private readonly ILibraryMonitor _libraryMonitor;
 55    private readonly ILogger<LibraryController> _logger;
 56    private readonly IServerConfigurationManager _serverConfigurationManager;
 57
 58    /// <summary>
 59    /// Initializes a new instance of the <see cref="LibraryController"/> class.
 60    /// </summary>
 61    /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
 62    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 63    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 64    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 65    /// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param>
 66    /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 67    /// <param name="libraryMonitor">Instance of the <see cref="ILibraryMonitor"/> interface.</param>
 68    /// <param name="logger">Instance of the <see cref="ILogger{LibraryController}"/> interface.</param>
 69    /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</p
 1470    public LibraryController(
 1471        IProviderManager providerManager,
 1472        ILibraryManager libraryManager,
 1473        IUserManager userManager,
 1474        IDtoService dtoService,
 1475        IActivityManager activityManager,
 1476        ILocalizationManager localization,
 1477        ILibraryMonitor libraryMonitor,
 1478        ILogger<LibraryController> logger,
 1479        IServerConfigurationManager serverConfigurationManager)
 80    {
 1481        _providerManager = providerManager;
 1482        _libraryManager = libraryManager;
 1483        _userManager = userManager;
 1484        _dtoService = dtoService;
 1485        _activityManager = activityManager;
 1486        _localization = localization;
 1487        _libraryMonitor = libraryMonitor;
 1488        _logger = logger;
 1489        _serverConfigurationManager = serverConfigurationManager;
 1490    }
 91
 92    /// <summary>
 93    /// Get the original file of an item.
 94    /// </summary>
 95    /// <param name="itemId">The item id.</param>
 96    /// <response code="200">File stream returned.</response>
 97    /// <response code="404">Item not found.</response>
 98    /// <returns>A <see cref="FileStreamResult"/> with the original file.</returns>
 99    [HttpGet("Items/{itemId}/File")]
 100    [Authorize]
 101    [ProducesResponseType(StatusCodes.Status200OK)]
 102    [ProducesResponseType(StatusCodes.Status404NotFound)]
 103    [ProducesFile("video/*", "audio/*")]
 104    public ActionResult GetFile([FromRoute, Required] Guid itemId)
 105    {
 1106        var item = _libraryManager.GetItemById<BaseItem>(itemId, User.GetUserId());
 1107        if (item is null)
 108        {
 1109            return NotFound();
 110        }
 111
 0112        return PhysicalFile(item.Path, MimeTypes.GetMimeType(item.Path), true);
 113    }
 114
 115    /// <summary>
 116    /// Gets critic review for an item.
 117    /// </summary>
 118    /// <response code="200">Critic reviews returned.</response>
 119    /// <returns>The list of critic reviews.</returns>
 120    [HttpGet("Items/{itemId}/CriticReviews")]
 121    [Authorize]
 122    [Obsolete("This endpoint is obsolete.")]
 123    [ProducesResponseType(StatusCodes.Status200OK)]
 124    public ActionResult<QueryResult<BaseItemDto>> GetCriticReviews()
 125    {
 126        return new QueryResult<BaseItemDto>();
 127    }
 128
 129    /// <summary>
 130    /// Get theme songs for an item.
 131    /// </summary>
 132    /// <param name="itemId">The item id.</param>
 133    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 134    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 135    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 136    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 137    /// <response code="200">Theme songs returned.</response>
 138    /// <response code="404">Item not found.</response>
 139    /// <returns>The item theme songs.</returns>
 140    [HttpGet("Items/{itemId}/ThemeSongs")]
 141    [Authorize]
 142    [ProducesResponseType(StatusCodes.Status200OK)]
 143    [ProducesResponseType(StatusCodes.Status404NotFound)]
 144    public ActionResult<ThemeMediaResult> GetThemeSongs(
 145        [FromRoute, Required] Guid itemId,
 146        [FromQuery] Guid? userId,
 147        [FromQuery] bool inheritFromParent = false,
 148        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 149        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 150    {
 2151        userId = RequestHelpers.GetUserId(User, userId);
 2152        var user = userId.IsNullOrEmpty()
 2153            ? null
 2154            : _userManager.GetUserById(userId.Value);
 155
 2156        var item = itemId.IsEmpty()
 2157            ? (userId.IsNullOrEmpty()
 2158                ? _libraryManager.RootFolder
 2159                : _libraryManager.GetUserRootFolder())
 2160            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 2161        if (item is null)
 162        {
 2163            return NotFound();
 164        }
 165
 0166        sortOrder ??= [];
 0167        sortBy ??= [];
 0168        var orderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder);
 169
 170        IReadOnlyList<BaseItem> themeItems;
 171
 0172        while (true)
 173        {
 0174            themeItems = item.GetThemeSongs(user, orderBy);
 175
 0176            if (themeItems.Count > 0 || !inheritFromParent)
 177            {
 178                break;
 179            }
 180
 0181            var parent = item.GetParent();
 0182            if (parent is null)
 183            {
 184                break;
 185            }
 186
 0187            item = parent;
 188        }
 189
 0190        var dtoOptions = new DtoOptions().AddClientFields(User);
 0191        var items = themeItems
 0192            .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
 0193            .ToArray();
 194
 0195        return new ThemeMediaResult
 0196        {
 0197            Items = items,
 0198            TotalRecordCount = items.Length,
 0199            OwnerId = item.Id
 0200        };
 201    }
 202
 203    /// <summary>
 204    /// Get theme videos for an item.
 205    /// </summary>
 206    /// <param name="itemId">The item id.</param>
 207    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 208    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 209    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 210    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 211    /// <response code="200">Theme videos returned.</response>
 212    /// <response code="404">Item not found.</response>
 213    /// <returns>The item theme videos.</returns>
 214    [HttpGet("Items/{itemId}/ThemeVideos")]
 215    [Authorize]
 216    [ProducesResponseType(StatusCodes.Status200OK)]
 217    [ProducesResponseType(StatusCodes.Status404NotFound)]
 218    public ActionResult<ThemeMediaResult> GetThemeVideos(
 219        [FromRoute, Required] Guid itemId,
 220        [FromQuery] Guid? userId,
 221        [FromQuery] bool inheritFromParent = false,
 222        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 223        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 224    {
 2225        userId = RequestHelpers.GetUserId(User, userId);
 2226        var user = userId.IsNullOrEmpty()
 2227            ? null
 2228            : _userManager.GetUserById(userId.Value);
 2229        var item = itemId.IsEmpty()
 2230            ? (userId.IsNullOrEmpty()
 2231                ? _libraryManager.RootFolder
 2232                : _libraryManager.GetUserRootFolder())
 2233            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 2234        if (item is null)
 235        {
 2236            return NotFound();
 237        }
 238
 0239        sortOrder ??= [];
 0240        sortBy ??= [];
 0241        var orderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder);
 242
 243        IEnumerable<BaseItem> themeItems;
 244
 0245        while (true)
 246        {
 0247            themeItems = item.GetThemeVideos(user, orderBy);
 248
 0249            if (themeItems.Any() || !inheritFromParent)
 250            {
 251                break;
 252            }
 253
 0254            var parent = item.GetParent();
 0255            if (parent is null)
 256            {
 257                break;
 258            }
 259
 0260            item = parent;
 261        }
 262
 0263        var dtoOptions = new DtoOptions().AddClientFields(User);
 0264        var items = themeItems
 0265            .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
 0266            .ToArray();
 267
 0268        return new ThemeMediaResult
 0269        {
 0270            Items = items,
 0271            TotalRecordCount = items.Length,
 0272            OwnerId = item.Id
 0273        };
 274    }
 275
 276    /// <summary>
 277    /// Get theme songs and videos for an item.
 278    /// </summary>
 279    /// <param name="itemId">The item id.</param>
 280    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 281    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 282    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 283    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 284    /// <response code="200">Theme songs and videos returned.</response>
 285    /// <response code="404">Item not found.</response>
 286    /// <returns>The item theme videos.</returns>
 287    [HttpGet("Items/{itemId}/ThemeMedia")]
 288    [Authorize]
 289    [ProducesResponseType(StatusCodes.Status200OK)]
 290    public ActionResult<AllThemeMediaResult> GetThemeMedia(
 291        [FromRoute, Required] Guid itemId,
 292        [FromQuery] Guid? userId,
 293        [FromQuery] bool inheritFromParent = false,
 294        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 295        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 296    {
 1297        var themeSongs = GetThemeSongs(
 1298            itemId,
 1299            userId,
 1300            inheritFromParent,
 1301            sortBy,
 1302            sortOrder);
 303
 1304        var themeVideos = GetThemeVideos(
 1305            itemId,
 1306            userId,
 1307            inheritFromParent,
 1308            sortBy,
 1309            sortOrder);
 310
 1311        if (themeSongs.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound }
 1312            || themeVideos.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound })
 313        {
 1314            return NotFound();
 315        }
 316
 0317        return new AllThemeMediaResult
 0318        {
 0319            ThemeSongsResult = themeSongs.Value,
 0320            ThemeVideosResult = themeVideos.Value,
 0321            SoundtrackSongsResult = new ThemeMediaResult()
 0322        };
 323    }
 324
 325    /// <summary>
 326    /// Starts a library scan.
 327    /// </summary>
 328    /// <response code="204">Library scan started.</response>
 329    /// <returns>A <see cref="NoContentResult"/>.</returns>
 330    [HttpPost("Library/Refresh")]
 331    [Authorize(Policy = Policies.RequiresElevation)]
 332    [ProducesResponseType(StatusCodes.Status204NoContent)]
 333    public async Task<ActionResult> RefreshLibrary()
 334    {
 335        try
 336        {
 337            await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(fa
 338        }
 339        catch (Exception ex)
 340        {
 341            _logger.LogError(ex, "Error refreshing library");
 342        }
 343
 344        return NoContent();
 345    }
 346
 347    /// <summary>
 348    /// Deletes an item from the library and filesystem.
 349    /// </summary>
 350    /// <param name="itemId">The item id.</param>
 351    /// <response code="204">Item deleted.</response>
 352    /// <response code="401">Unauthorized access.</response>
 353    /// <response code="404">Item not found.</response>
 354    /// <returns>A <see cref="NoContentResult"/>.</returns>
 355    [HttpDelete("Items/{itemId}")]
 356    [Authorize]
 357    [ProducesResponseType(StatusCodes.Status204NoContent)]
 358    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 359    [ProducesResponseType(StatusCodes.Status404NotFound)]
 360    public ActionResult DeleteItem(Guid itemId)
 361    {
 1362        var userId = User.GetUserId();
 1363        var isApiKey = User.GetIsApiKey();
 1364        var user = userId.IsEmpty() && isApiKey
 1365            ? null
 1366            : _userManager.GetUserById(userId);
 367
 1368        if (user is null && !isApiKey)
 369        {
 0370            return NotFound();
 371        }
 372
 1373        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1374        if (item is null)
 375        {
 1376            return NotFound();
 377        }
 378
 0379        if (user is not null && !item.CanDelete(user))
 380        {
 0381            return Unauthorized("Unauthorized access");
 382        }
 383
 0384        _libraryManager.DeleteItem(
 0385            item,
 0386            new DeleteOptions { DeleteFileLocation = true },
 0387            true);
 388
 0389        return NoContent();
 390    }
 391
 392    /// <summary>
 393    /// Deletes items from the library and filesystem.
 394    /// </summary>
 395    /// <param name="ids">The item ids.</param>
 396    /// <response code="204">Items deleted.</response>
 397    /// <response code="401">Unauthorized access.</response>
 398    /// <returns>A <see cref="NoContentResult"/>.</returns>
 399    [HttpDelete("Items")]
 400    [Authorize]
 401    [ProducesResponseType(StatusCodes.Status204NoContent)]
 402    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 403    [ProducesResponseType(StatusCodes.Status404NotFound)]
 404    public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids)
 405    {
 1406        var isApiKey = User.GetIsApiKey();
 1407        var userId = User.GetUserId();
 1408        var user = !isApiKey && !userId.IsEmpty()
 1409            ? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException()
 1410            : null;
 411
 1412        if (!isApiKey && user is null)
 413        {
 0414            return Unauthorized("Unauthorized access");
 415        }
 416
 3417        foreach (var i in ids)
 418        {
 1419            var item = _libraryManager.GetItemById<BaseItem>(i, user);
 1420            if (item is null)
 421            {
 1422                return NotFound();
 423            }
 424
 0425            if (user is not null && !item.CanDelete(user))
 426            {
 0427                return Unauthorized("Unauthorized access");
 428            }
 429
 0430            _libraryManager.DeleteItem(
 0431                item,
 0432                new DeleteOptions { DeleteFileLocation = true },
 0433                true);
 434        }
 435
 0436        return NoContent();
 437    }
 438
 439    /// <summary>
 440    /// Get item counts.
 441    /// </summary>
 442    /// <param name="userId">Optional. Get counts from a specific user's library.</param>
 443    /// <param name="isFavorite">Optional. Get counts of favorite items.</param>
 444    /// <response code="200">Item counts returned.</response>
 445    /// <returns>Item counts.</returns>
 446    [HttpGet("Items/Counts")]
 447    [Authorize]
 448    [ProducesResponseType(StatusCodes.Status200OK)]
 449    public ActionResult<ItemCounts> GetItemCounts(
 450        [FromQuery] Guid? userId,
 451        [FromQuery] bool? isFavorite)
 452    {
 0453        userId = RequestHelpers.GetUserId(User, userId);
 0454        var user = userId.IsNullOrEmpty()
 0455            ? null
 0456            : _userManager.GetUserById(userId.Value);
 457
 0458        var counts = new ItemCounts
 0459        {
 0460            AlbumCount = GetCount(BaseItemKind.MusicAlbum, user, isFavorite),
 0461            EpisodeCount = GetCount(BaseItemKind.Episode, user, isFavorite),
 0462            MovieCount = GetCount(BaseItemKind.Movie, user, isFavorite),
 0463            SeriesCount = GetCount(BaseItemKind.Series, user, isFavorite),
 0464            SongCount = GetCount(BaseItemKind.Audio, user, isFavorite),
 0465            MusicVideoCount = GetCount(BaseItemKind.MusicVideo, user, isFavorite),
 0466            BoxSetCount = GetCount(BaseItemKind.BoxSet, user, isFavorite),
 0467            BookCount = GetCount(BaseItemKind.Book, user, isFavorite)
 0468        };
 469
 0470        return counts;
 471    }
 472
 473    /// <summary>
 474    /// Gets all parents of an item.
 475    /// </summary>
 476    /// <param name="itemId">The item id.</param>
 477    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 478    /// <response code="200">Item parents returned.</response>
 479    /// <response code="404">Item not found.</response>
 480    /// <returns>Item parents.</returns>
 481    [HttpGet("Items/{itemId}/Ancestors")]
 482    [Authorize]
 483    [ProducesResponseType(StatusCodes.Status200OK)]
 484    [ProducesResponseType(StatusCodes.Status404NotFound)]
 485    public ActionResult<IEnumerable<BaseItemDto>> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? user
 486    {
 1487        userId = RequestHelpers.GetUserId(User, userId);
 1488        var user = userId.IsNullOrEmpty()
 1489            ? null
 1490            : _userManager.GetUserById(userId.Value);
 1491        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1492        if (item is null)
 493        {
 1494            return NotFound();
 495        }
 496
 0497        var baseItemDtos = new List<BaseItemDto>();
 498
 0499        var dtoOptions = new DtoOptions().AddClientFields(User);
 0500        BaseItem? parent = item.GetParent();
 501
 0502        while (parent is not null)
 503        {
 0504            if (user is not null)
 505            {
 0506                parent = TranslateParentItem(parent, user);
 0507                if (parent is null)
 508                {
 509                    break;
 510                }
 511            }
 512
 0513            baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
 514
 0515            parent = parent.GetParent();
 516        }
 517
 0518        return baseItemDtos;
 519    }
 520
 521    /// <summary>
 522    /// Gets a list of physical paths from virtual folders.
 523    /// </summary>
 524    /// <response code="200">Physical paths returned.</response>
 525    /// <returns>List of physical paths.</returns>
 526    [HttpGet("Library/PhysicalPaths")]
 527    [Authorize(Policy = Policies.RequiresElevation)]
 528    [ProducesResponseType(StatusCodes.Status200OK)]
 529    public ActionResult<IEnumerable<string>> GetPhysicalPaths()
 530    {
 0531        return Ok(_libraryManager.RootFolder.Children
 0532            .SelectMany(c => c.PhysicalLocations));
 533    }
 534
 535    /// <summary>
 536    /// Gets all user media folders.
 537    /// </summary>
 538    /// <param name="isHidden">Optional. Filter by folders that are marked hidden, or not.</param>
 539    /// <response code="200">Media folders returned.</response>
 540    /// <returns>List of user media folders.</returns>
 541    [HttpGet("Library/MediaFolders")]
 542    [Authorize(Policy = Policies.RequiresElevation)]
 543    [ProducesResponseType(StatusCodes.Status200OK)]
 544    public ActionResult<QueryResult<BaseItemDto>> GetMediaFolders([FromQuery] bool? isHidden)
 545    {
 0546        var items = _libraryManager.GetUserRootFolder().Children
 0547            .Concat(_libraryManager.RootFolder.VirtualChildren)
 0548            .Where(i => _libraryManager.GetLibraryOptions(i).Enabled)
 0549            .OrderBy(i => i.SortName)
 0550            .ToList();
 551
 0552        if (isHidden.HasValue)
 553        {
 0554            var val = isHidden.Value;
 555
 0556            items = items.Where(i => i.IsHidden == val).ToList();
 557        }
 558
 0559        var dtoOptions = new DtoOptions().AddClientFields(User);
 0560        var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions);
 0561        return new QueryResult<BaseItemDto>(resultArray);
 562    }
 563
 564    /// <summary>
 565    /// Reports that new episodes of a series have been added by an external source.
 566    /// </summary>
 567    /// <param name="tvdbId">The tvdbId.</param>
 568    /// <response code="204">Report success.</response>
 569    /// <returns>A <see cref="NoContentResult"/>.</returns>
 570    [HttpPost("Library/Series/Added", Name = "PostAddedSeries")]
 571    [HttpPost("Library/Series/Updated")]
 572    [Authorize]
 573    [ProducesResponseType(StatusCodes.Status204NoContent)]
 574    public ActionResult PostUpdatedSeries([FromQuery] string? tvdbId)
 575    {
 0576        var series = _libraryManager.GetItemList(new InternalItemsQuery
 0577        {
 0578            IncludeItemTypes = new[] { BaseItemKind.Series },
 0579            DtoOptions = new DtoOptions(false)
 0580            {
 0581                EnableImages = false
 0582            }
 0583        }).Where(i => string.Equals(tvdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Tvdb), StringCo
 584
 0585        foreach (var item in series)
 586        {
 0587            _libraryMonitor.ReportFileSystemChanged(item.Path);
 588        }
 589
 0590        return NoContent();
 591    }
 592
 593    /// <summary>
 594    /// Reports that new movies have been added by an external source.
 595    /// </summary>
 596    /// <param name="tmdbId">The tmdbId.</param>
 597    /// <param name="imdbId">The imdbId.</param>
 598    /// <response code="204">Report success.</response>
 599    /// <returns>A <see cref="NoContentResult"/>.</returns>
 600    [HttpPost("Library/Movies/Added", Name = "PostAddedMovies")]
 601    [HttpPost("Library/Movies/Updated")]
 602    [Authorize]
 603    [ProducesResponseType(StatusCodes.Status204NoContent)]
 604    public ActionResult PostUpdatedMovies([FromQuery] string? tmdbId, [FromQuery] string? imdbId)
 605    {
 0606        var movies = _libraryManager.GetItemList(new InternalItemsQuery
 0607        {
 0608            IncludeItemTypes = new[] { BaseItemKind.Movie },
 0609            DtoOptions = new DtoOptions(false)
 0610            {
 0611                EnableImages = false
 0612            }
 0613        });
 614
 0615        if (!string.IsNullOrWhiteSpace(imdbId))
 616        {
 0617            movies = movies.Where(i => string.Equals(imdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvide
 618        }
 0619        else if (!string.IsNullOrWhiteSpace(tmdbId))
 620        {
 0621            movies = movies.Where(i => string.Equals(tmdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvide
 622        }
 623        else
 624        {
 0625            movies = new List<BaseItem>();
 626        }
 627
 0628        foreach (var item in movies)
 629        {
 0630            _libraryMonitor.ReportFileSystemChanged(item.Path);
 631        }
 632
 0633        return NoContent();
 634    }
 635
 636    /// <summary>
 637    /// Reports that new movies have been added by an external source.
 638    /// </summary>
 639    /// <param name="dto">The update paths.</param>
 640    /// <response code="204">Report success.</response>
 641    /// <returns>A <see cref="NoContentResult"/>.</returns>
 642    [HttpPost("Library/Media/Updated")]
 643    [Authorize]
 644    [ProducesResponseType(StatusCodes.Status204NoContent)]
 645    public ActionResult PostUpdatedMedia([FromBody, Required] MediaUpdateInfoDto dto)
 646    {
 0647        foreach (var item in dto.Updates)
 648        {
 0649            _libraryMonitor.ReportFileSystemChanged(item.Path ?? throw new ArgumentException("Item path can't be null.")
 650        }
 651
 0652        return NoContent();
 653    }
 654
 655    /// <summary>
 656    /// Downloads item media.
 657    /// </summary>
 658    /// <param name="itemId">The item id.</param>
 659    /// <response code="200">Media downloaded.</response>
 660    /// <response code="404">Item not found.</response>
 661    /// <returns>A <see cref="FileResult"/> containing the media stream.</returns>
 662    /// <exception cref="ArgumentException">User can't download or item can't be downloaded.</exception>
 663    [HttpGet("Items/{itemId}/Download")]
 664    [Authorize(Policy = Policies.Download)]
 665    [ProducesResponseType(StatusCodes.Status200OK)]
 666    [ProducesResponseType(StatusCodes.Status404NotFound)]
 667    [ProducesFile("video/*", "audio/*")]
 668    public async Task<ActionResult> GetDownload([FromRoute, Required] Guid itemId)
 669    {
 670        var userId = User.GetUserId();
 671        var user = userId.IsEmpty()
 672            ? null
 673            : _userManager.GetUserById(userId);
 674        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 675        if (item is null)
 676        {
 677            return NotFound();
 678        }
 679
 680        if (user is not null)
 681        {
 682            if (!item.CanDownload(user))
 683            {
 684                throw new ArgumentException("Item does not support downloading");
 685            }
 686        }
 687        else
 688        {
 689            if (!item.CanDownload())
 690            {
 691                throw new ArgumentException("Item does not support downloading");
 692            }
 693        }
 694
 695        if (user is not null)
 696        {
 697            await LogDownloadAsync(item, user).ConfigureAwait(false);
 698        }
 699
 700        // Quotes are valid in linux. They'll possibly cause issues here.
 701        var filename = Path.GetFileName(item.Path)?.Replace("\"", string.Empty, StringComparison.Ordinal);
 702
 703        return PhysicalFile(item.Path, MimeTypes.GetMimeType(item.Path), filename, true);
 704    }
 705
 706    /// <summary>
 707    /// Gets similar items.
 708    /// </summary>
 709    /// <param name="itemId">The item id.</param>
 710    /// <param name="excludeArtistIds">Exclude artist ids.</param>
 711    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 712    /// <param name="limit">Optional. The maximum number of records to return.</param>
 713    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 714    /// <response code="200">Similar items returned.</response>
 715    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> containing the similar items.</returns>
 716    [HttpGet("Artists/{itemId}/Similar", Name = "GetSimilarArtists")]
 717    [HttpGet("Items/{itemId}/Similar")]
 718    [HttpGet("Albums/{itemId}/Similar", Name = "GetSimilarAlbums")]
 719    [HttpGet("Shows/{itemId}/Similar", Name = "GetSimilarShows")]
 720    [HttpGet("Movies/{itemId}/Similar", Name = "GetSimilarMovies")]
 721    [HttpGet("Trailers/{itemId}/Similar", Name = "GetSimilarTrailers")]
 722    [Authorize]
 723    [ProducesResponseType(StatusCodes.Status200OK)]
 724    public ActionResult<QueryResult<BaseItemDto>> GetSimilarItems(
 725        [FromRoute, Required] Guid itemId,
 726        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 727        [FromQuery] Guid? userId,
 728        [FromQuery] int? limit,
 729        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields)
 730    {
 6731        userId = RequestHelpers.GetUserId(User, userId);
 6732        var user = userId.IsNullOrEmpty()
 6733            ? null
 6734            : _userManager.GetUserById(userId.Value);
 6735        var item = itemId.IsEmpty()
 6736            ? (user is null
 6737                ? _libraryManager.RootFolder
 6738                : _libraryManager.GetUserRootFolder())
 6739            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 6740        if (item is null)
 741        {
 6742            return NotFound();
 743        }
 744
 0745        if (item is Episode || (item is IItemByName && item is not MusicArtist))
 746        {
 0747            return new QueryResult<BaseItemDto>();
 748        }
 749
 0750        var dtoOptions = new DtoOptions { Fields = fields }
 0751            .AddClientFields(User);
 752
 0753        var program = item as IHasProgramAttributes;
 0754        bool? isMovie = item is Movie || (program is not null && program.IsMovie) || item is Trailer;
 0755        bool? isSeries = item is Series || (program is not null && program.IsSeries);
 756
 0757        var includeItemTypes = new List<BaseItemKind>();
 0758        if (isMovie.Value)
 759        {
 0760            includeItemTypes.Add(BaseItemKind.Movie);
 0761            if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
 762            {
 0763                includeItemTypes.Add(BaseItemKind.Trailer);
 0764                includeItemTypes.Add(BaseItemKind.LiveTvProgram);
 765            }
 766        }
 0767        else if (isSeries.Value)
 768        {
 0769            includeItemTypes.Add(BaseItemKind.Series);
 770        }
 771        else
 772        {
 773            // For non series and movie types these columns are typically null
 774            // isSeries = null;
 0775            isMovie = null;
 0776            includeItemTypes.Add(item.GetBaseItemKind());
 777        }
 778
 0779        var query = new InternalItemsQuery(user)
 0780        {
 0781            Genres = item.Genres,
 0782            Limit = limit,
 0783            IncludeItemTypes = includeItemTypes.ToArray(),
 0784            DtoOptions = dtoOptions,
 0785            EnableTotalRecordCount = !isMovie ?? true,
 0786            EnableGroupByMetadataKey = isMovie ?? false,
 0787        };
 788
 789        // ExcludeArtistIds
 0790        if (excludeArtistIds.Length != 0)
 791        {
 0792            query.ExcludeArtistIds = excludeArtistIds;
 793        }
 794
 0795        var itemsResult = _libraryManager.GetItemList(query);
 796
 0797        var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
 798
 0799        return new QueryResult<BaseItemDto>(
 0800            query.StartIndex,
 0801            itemsResult.Count,
 0802            returnList);
 803    }
 804
 805    /// <summary>
 806    /// Gets the library options info.
 807    /// </summary>
 808    /// <param name="libraryContentType">Library content type.</param>
 809    /// <param name="isNewLibrary">Whether this is a new library.</param>
 810    /// <response code="200">Library options info returned.</response>
 811    /// <returns>Library options info.</returns>
 812    [HttpGet("Libraries/AvailableOptions")]
 813    [Authorize(Policy = Policies.FirstTimeSetupOrDefault)]
 814    [ProducesResponseType(StatusCodes.Status200OK)]
 815    public ActionResult<LibraryOptionsResultDto> GetLibraryOptionsInfo(
 816        [FromQuery] CollectionType? libraryContentType,
 817        [FromQuery] bool isNewLibrary = false)
 818    {
 0819        var result = new LibraryOptionsResultDto();
 820
 0821        var types = GetRepresentativeItemTypes(libraryContentType);
 0822        var typesList = types.ToList();
 823
 0824        var plugins = _providerManager.GetAllMetadataPlugins()
 0825            .Where(i => types.Contains(i.ItemType, StringComparison.OrdinalIgnoreCase))
 0826            .OrderBy(i => typesList.IndexOf(i.ItemType))
 0827            .ToList();
 828
 0829        result.MetadataSavers = plugins
 0830            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataSaver))
 0831            .Select(i => new LibraryOptionInfoDto
 0832            {
 0833                Name = i.Name,
 0834                DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary)
 0835            })
 0836            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0837            .ToArray();
 838
 0839        result.MetadataReaders = plugins
 0840            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LocalMetadataProvider))
 0841            .Select(i => new LibraryOptionInfoDto
 0842            {
 0843                Name = i.Name,
 0844                DefaultEnabled = true
 0845            })
 0846            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0847            .ToArray();
 848
 0849        result.SubtitleFetchers = plugins
 0850            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.SubtitleFetcher))
 0851            .Select(i => new LibraryOptionInfoDto
 0852            {
 0853                Name = i.Name,
 0854                DefaultEnabled = true
 0855            })
 0856            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0857            .ToArray();
 858
 0859        result.LyricFetchers = plugins
 0860            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LyricFetcher))
 0861            .Select(i => new LibraryOptionInfoDto
 0862            {
 0863                Name = i.Name,
 0864                DefaultEnabled = true
 0865            })
 0866            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0867            .ToArray();
 868
 0869        result.MediaSegmentProviders = plugins
 0870            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MediaSegmentProvider))
 0871            .Select(i => new LibraryOptionInfoDto
 0872            {
 0873                Name = i.Name,
 0874                DefaultEnabled = true
 0875            })
 0876            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0877            .ToArray();
 878
 0879        var typeOptions = new List<LibraryTypeOptionsDto>();
 880
 0881        foreach (var type in types)
 882        {
 0883            TypeOptions.DefaultImageOptions.TryGetValue(type, out var defaultImageOptions);
 884
 0885            typeOptions.Add(new LibraryTypeOptionsDto
 0886            {
 0887                Type = type,
 0888
 0889                MetadataFetchers = plugins
 0890                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0891                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher))
 0892                    .Select(i => new LibraryOptionInfoDto
 0893                    {
 0894                        Name = i.Name,
 0895                        DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
 0896                    })
 0897                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0898                    .ToArray(),
 0899
 0900                ImageFetchers = plugins
 0901                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0902                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher))
 0903                    .Select(i => new LibraryOptionInfoDto
 0904                    {
 0905                        Name = i.Name,
 0906                        DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
 0907                    })
 0908                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0909                    .ToArray(),
 0910
 0911                SupportedImageTypes = plugins
 0912                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0913                    .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>())
 0914                    .Distinct()
 0915                    .ToArray(),
 0916
 0917                DefaultImageOptions = defaultImageOptions ?? Array.Empty<ImageOption>()
 0918            });
 919        }
 920
 0921        result.TypeOptions = typeOptions.ToArray();
 922
 0923        return result;
 924    }
 925
 926    private int GetCount(BaseItemKind itemKind, User? user, bool? isFavorite)
 927    {
 0928        var query = new InternalItemsQuery(user)
 0929        {
 0930            IncludeItemTypes = new[] { itemKind },
 0931            Limit = 0,
 0932            Recursive = true,
 0933            IsVirtualItem = false,
 0934            IsFavorite = isFavorite,
 0935            DtoOptions = new DtoOptions(false)
 0936            {
 0937                EnableImages = false
 0938            }
 0939        };
 940
 0941        return _libraryManager.GetItemsResult(query).TotalRecordCount;
 942    }
 943
 944    private BaseItem? TranslateParentItem(BaseItem item, User user)
 945    {
 0946        return item.GetParent() is AggregateFolder
 0947            ? _libraryManager.GetUserRootFolder().GetChildren(user, true)
 0948                .FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path))
 0949            : item;
 950    }
 951
 952    private async Task LogDownloadAsync(BaseItem item, User user)
 953    {
 954        try
 955        {
 956            await _activityManager.CreateAsync(new ActivityLog(
 957                string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithVal
 958                "UserDownloadingContent",
 959                User.GetUserId())
 960            {
 961                ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceV
 962                ItemId = item.Id.ToString("N", CultureInfo.InvariantCulture)
 963            }).ConfigureAwait(false);
 964        }
 965        catch
 966        {
 967            // Logged at lower levels
 968        }
 969    }
 970
 971    private static string[] GetRepresentativeItemTypes(CollectionType? contentType)
 972    {
 0973        return contentType switch
 0974        {
 0975            CollectionType.boxsets => new[] { "BoxSet" },
 0976            CollectionType.playlists => new[] { "Playlist" },
 0977            CollectionType.movies => new[] { "Movie" },
 0978            CollectionType.tvshows => new[] { "Series", "Season", "Episode" },
 0979            CollectionType.books => new[] { "Book" },
 0980            CollectionType.music => new[] { "MusicArtist", "MusicAlbum", "Audio", "MusicVideo" },
 0981            CollectionType.homevideos => new[] { "Video", "Photo" },
 0982            CollectionType.photos => new[] { "Video", "Photo" },
 0983            CollectionType.musicvideos => new[] { "MusicVideo" },
 0984            _ => new[] { "Series", "Season", "Episode", "Movie" }
 0985        };
 986    }
 987
 988    private bool IsSaverEnabledByDefault(string name, string[] itemTypes, bool isNewLibrary)
 989    {
 0990        if (isNewLibrary)
 991        {
 0992            return false;
 993        }
 994
 0995        var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
 0996            .Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
 0997            .ToArray();
 998
 0999        return metadataOptions.Length == 0 || metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringCo
 1000    }
 1001
 1002    private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
 1003    {
 01004        if (isNewLibrary)
 1005        {
 01006            if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
 1007            {
 01008                return !(string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
 01009                         || string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
 01010                         || string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase));
 1011            }
 1012
 01013            return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
 01014                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
 01015                   || string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase);
 1016        }
 1017
 01018        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
 01019        return metadataOptions is null || !metadataOptions.DisabledMetadataFetchers.Contains(name, StringComparison.Ordi
 1020    }
 1021
 1022    private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
 1023    {
 01024        if (isNewLibrary)
 1025        {
 01026            if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
 1027            {
 01028                return !string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase)
 01029                       && !string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
 01030                       && !string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
 01031                       && !string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase);
 1032            }
 1033
 01034            return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
 01035                   || string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase)
 01036                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
 01037                   || string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase);
 1038        }
 1039
 01040        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
 01041        return metadataOptions is null || !metadataOptions.DisabledImageFetchers.Contains(name, StringComparison.Ordinal
 1042    }
 1043}

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[])
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)
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)
GetCount(Jellyfin.Data.Enums.BaseItemKind,Jellyfin.Database.Implementations.Entities.User,System.Nullable`1<System.Boolean>)
TranslateParentItem(MediaBrowser.Controller.Entities.BaseItem,Jellyfin.Database.Implementations.Entities.User)
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)