< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.LibraryController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/LibraryController.cs
Line coverage
25%
Covered lines: 118
Uncovered lines: 348
Coverable lines: 466
Total lines: 1088
Line coverage: 25.3%
Branch coverage
15%
Covered branches: 32
Total branches: 208
Branch coverage: 15.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 3/6/2026 - 12:14:09 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: 10245/16/2026 - 12:15:55 AM Line coverage: 24.8% (108/434) Branch coverage: 15.1% (29/192) Total lines: 10065/30/2026 - 12:15:32 AM Line coverage: 25.5% (118/461) Branch coverage: 15.8% (32/202) Total lines: 10776/2/2026 - 12:15:49 AM Line coverage: 25.3% (118/466) Branch coverage: 15.3% (32/208) Total lines: 1088 3/6/2026 - 12:14:09 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: 10245/16/2026 - 12:15:55 AM Line coverage: 24.8% (108/434) Branch coverage: 15.1% (29/192) Total lines: 10065/30/2026 - 12:15:32 AM Line coverage: 25.5% (118/461) Branch coverage: 15.8% (32/202) Total lines: 10776/2/2026 - 12:15:49 AM Line coverage: 25.3% (118/466) Branch coverage: 15.3% (32/208) Total lines: 1088

Coverage delta

Coverage delta 2 -2

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetFile(...)12.5%27833.33%
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%
GetItemCollections(...)30%411032%
GetSimilarItems()21.42%541441.37%
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.Collections;
 21using MediaBrowser.Controller.Configuration;
 22using MediaBrowser.Controller.Dto;
 23using MediaBrowser.Controller.Entities;
 24using MediaBrowser.Controller.Entities.Audio;
 25using MediaBrowser.Controller.Entities.Movies;
 26using MediaBrowser.Controller.Entities.TV;
 27using MediaBrowser.Controller.IO;
 28using MediaBrowser.Controller.Library;
 29using MediaBrowser.Controller.Providers;
 30using MediaBrowser.Model.Activity;
 31using MediaBrowser.Model.Configuration;
 32using MediaBrowser.Model.Dto;
 33using MediaBrowser.Model.Entities;
 34using MediaBrowser.Model.Globalization;
 35using MediaBrowser.Model.Net;
 36using MediaBrowser.Model.Querying;
 37using Microsoft.AspNetCore.Authorization;
 38using Microsoft.AspNetCore.Http;
 39using Microsoft.AspNetCore.Mvc;
 40using Microsoft.Extensions.Logging;
 41
 42namespace Jellyfin.Api.Controllers;
 43
 44/// <summary>
 45/// Library Controller.
 46/// </summary>
 47[Route("")]
 48public class LibraryController : BaseJellyfinApiController
 49{
 50    private readonly IProviderManager _providerManager;
 51    private readonly ISimilarItemsManager _similarItemsManager;
 52    private readonly ILibraryManager _libraryManager;
 53    private readonly IUserManager _userManager;
 54    private readonly ICollectionManager _collectionManager;
 55    private readonly IDtoService _dtoService;
 56    private readonly IActivityManager _activityManager;
 57    private readonly ILocalizationManager _localization;
 58    private readonly ILibraryMonitor _libraryMonitor;
 59    private readonly ILogger<LibraryController> _logger;
 60    private readonly IServerConfigurationManager _serverConfigurationManager;
 61
 62    /// <summary>
 63    /// Initializes a new instance of the <see cref="LibraryController"/> class.
 64    /// </summary>
 65    /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
 66    /// <param name="similarItemsManager">Instance of the <see cref="ISimilarItemsManager"/> interface.</param>
 67    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 68    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 69    /// <param name="collectionManager">Instance of the <see cref="ICollectionManager"/> interface.</param>
 70    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 71    /// <param name="activityManager">Instance of the <see cref="IActivityManager"/> interface.</param>
 72    /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 73    /// <param name="libraryMonitor">Instance of the <see cref="ILibraryMonitor"/> interface.</param>
 74    /// <param name="logger">Instance of the <see cref="ILogger{LibraryController}"/> interface.</param>
 75    /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</p
 1576    public LibraryController(
 1577        IProviderManager providerManager,
 1578        ISimilarItemsManager similarItemsManager,
 1579        ILibraryManager libraryManager,
 1580        IUserManager userManager,
 1581        ICollectionManager collectionManager,
 1582        IDtoService dtoService,
 1583        IActivityManager activityManager,
 1584        ILocalizationManager localization,
 1585        ILibraryMonitor libraryMonitor,
 1586        ILogger<LibraryController> logger,
 1587        IServerConfigurationManager serverConfigurationManager)
 88    {
 1589        _providerManager = providerManager;
 1590        _similarItemsManager = similarItemsManager;
 1591        _libraryManager = libraryManager;
 1592        _userManager = userManager;
 1593        _collectionManager = collectionManager;
 1594        _dtoService = dtoService;
 1595        _activityManager = activityManager;
 1596        _localization = localization;
 1597        _libraryMonitor = libraryMonitor;
 1598        _logger = logger;
 1599        _serverConfigurationManager = serverConfigurationManager;
 15100    }
 101
 102    /// <summary>
 103    /// Get the original file of an item.
 104    /// </summary>
 105    /// <param name="itemId">The item id.</param>
 106    /// <response code="200">File stream returned.</response>
 107    /// <response code="404">Item not found.</response>
 108    /// <returns>A <see cref="FileStreamResult"/> with the original file.</returns>
 109    [HttpGet("Items/{itemId}/File")]
 110    [Authorize]
 111    [ProducesResponseType(StatusCodes.Status200OK)]
 112    [ProducesResponseType(StatusCodes.Status404NotFound)]
 113    [ProducesFile("video/*", "audio/*")]
 114    public ActionResult GetFile([FromRoute, Required] Guid itemId)
 115    {
 1116        var item = _libraryManager.GetItemById<BaseItem>(itemId, User.GetUserId());
 1117        if (item is null)
 118        {
 1119            return NotFound();
 120        }
 121
 0122        var filePath = item.Path;
 0123        if (item.IsFileProtocol)
 124        {
 125            // PhysicalFile does not work well with symlinks at the moment.
 0126            var resolved = FileSystemHelper.ResolveLinkTarget(filePath, returnFinalTarget: true);
 0127            if (resolved is not null && resolved.Exists)
 128            {
 0129                filePath = resolved.FullName;
 130            }
 131        }
 132
 0133        return PhysicalFile(filePath, MimeTypes.GetMimeType(filePath), true);
 134    }
 135
 136    /// <summary>
 137    /// Get theme songs for an item.
 138    /// </summary>
 139    /// <param name="itemId">The item id.</param>
 140    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 141    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 142    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 143    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 144    /// <response code="200">Theme songs returned.</response>
 145    /// <response code="404">Item not found.</response>
 146    /// <returns>The item theme songs.</returns>
 147    [HttpGet("Items/{itemId}/ThemeSongs")]
 148    [Authorize]
 149    [ProducesResponseType(StatusCodes.Status200OK)]
 150    [ProducesResponseType(StatusCodes.Status404NotFound)]
 151    public ActionResult<ThemeMediaResult> GetThemeSongs(
 152        [FromRoute, Required] Guid itemId,
 153        [FromQuery] Guid? userId,
 154        [FromQuery] bool inheritFromParent = false,
 155        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 156        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 157    {
 2158        userId = RequestHelpers.GetUserId(User, userId);
 2159        var user = userId.IsNullOrEmpty()
 2160            ? null
 2161            : _userManager.GetUserById(userId.Value);
 162
 2163        var item = itemId.IsEmpty()
 2164            ? (userId.IsNullOrEmpty()
 2165                ? _libraryManager.RootFolder
 2166                : _libraryManager.GetUserRootFolder())
 2167            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 2168        if (item is null)
 169        {
 2170            return NotFound();
 171        }
 172
 0173        sortOrder ??= [];
 0174        sortBy ??= [];
 0175        var orderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder);
 176
 177        IReadOnlyList<BaseItem> themeItems;
 178
 0179        while (true)
 180        {
 0181            themeItems = item.GetThemeSongs(user, orderBy);
 182
 0183            if (themeItems.Count > 0 || !inheritFromParent)
 184            {
 185                break;
 186            }
 187
 0188            var parent = item.GetParent();
 0189            if (parent is null)
 190            {
 191                break;
 192            }
 193
 0194            item = parent;
 195        }
 196
 0197        var dtoOptions = new DtoOptions();
 0198        var items = themeItems
 0199            .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
 0200            .ToArray();
 201
 0202        return new ThemeMediaResult
 0203        {
 0204            Items = items,
 0205            TotalRecordCount = items.Length,
 0206            OwnerId = item.Id
 0207        };
 208    }
 209
 210    /// <summary>
 211    /// Get theme videos for an item.
 212    /// </summary>
 213    /// <param name="itemId">The item id.</param>
 214    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 215    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 216    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 217    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 218    /// <response code="200">Theme videos returned.</response>
 219    /// <response code="404">Item not found.</response>
 220    /// <returns>The item theme videos.</returns>
 221    [HttpGet("Items/{itemId}/ThemeVideos")]
 222    [Authorize]
 223    [ProducesResponseType(StatusCodes.Status200OK)]
 224    [ProducesResponseType(StatusCodes.Status404NotFound)]
 225    public ActionResult<ThemeMediaResult> GetThemeVideos(
 226        [FromRoute, Required] Guid itemId,
 227        [FromQuery] Guid? userId,
 228        [FromQuery] bool inheritFromParent = false,
 229        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 230        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 231    {
 2232        userId = RequestHelpers.GetUserId(User, userId);
 2233        var user = userId.IsNullOrEmpty()
 2234            ? null
 2235            : _userManager.GetUserById(userId.Value);
 2236        var item = itemId.IsEmpty()
 2237            ? (userId.IsNullOrEmpty()
 2238                ? _libraryManager.RootFolder
 2239                : _libraryManager.GetUserRootFolder())
 2240            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 2241        if (item is null)
 242        {
 2243            return NotFound();
 244        }
 245
 0246        sortOrder ??= [];
 0247        sortBy ??= [];
 0248        var orderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder);
 249
 250        IEnumerable<BaseItem> themeItems;
 251
 0252        while (true)
 253        {
 0254            themeItems = item.GetThemeVideos(user, orderBy);
 255
 0256            if (themeItems.Any() || !inheritFromParent)
 257            {
 258                break;
 259            }
 260
 0261            var parent = item.GetParent();
 0262            if (parent is null)
 263            {
 264                break;
 265            }
 266
 0267            item = parent;
 268        }
 269
 0270        var dtoOptions = new DtoOptions();
 0271        var items = themeItems
 0272            .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
 0273            .ToArray();
 274
 0275        return new ThemeMediaResult
 0276        {
 0277            Items = items,
 0278            TotalRecordCount = items.Length,
 0279            OwnerId = item.Id
 0280        };
 281    }
 282
 283    /// <summary>
 284    /// Get theme songs and videos for an item.
 285    /// </summary>
 286    /// <param name="itemId">The item id.</param>
 287    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 288    /// <param name="inheritFromParent">Optional. Determines whether or not parent items should be searched for theme me
 289    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 290    /// <param name="sortOrder">Optional. Sort Order - Ascending, Descending.</param>
 291    /// <response code="200">Theme songs and videos returned.</response>
 292    /// <response code="404">Item not found.</response>
 293    /// <returns>The item theme videos.</returns>
 294    [HttpGet("Items/{itemId}/ThemeMedia")]
 295    [Authorize]
 296    [ProducesResponseType(StatusCodes.Status200OK)]
 297    public ActionResult<AllThemeMediaResult> GetThemeMedia(
 298        [FromRoute, Required] Guid itemId,
 299        [FromQuery] Guid? userId,
 300        [FromQuery] bool inheritFromParent = false,
 301        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[]? sortBy = null,
 302        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[]? sortOrder = null)
 303    {
 1304        var themeSongs = GetThemeSongs(
 1305            itemId,
 1306            userId,
 1307            inheritFromParent,
 1308            sortBy,
 1309            sortOrder);
 310
 1311        var themeVideos = GetThemeVideos(
 1312            itemId,
 1313            userId,
 1314            inheritFromParent,
 1315            sortBy,
 1316            sortOrder);
 317
 1318        if (themeSongs.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound }
 1319            || themeVideos.Result is StatusCodeResult { StatusCode: StatusCodes.Status404NotFound })
 320        {
 1321            return NotFound();
 322        }
 323
 0324        return new AllThemeMediaResult
 0325        {
 0326            ThemeSongsResult = themeSongs.Value,
 0327            ThemeVideosResult = themeVideos.Value,
 0328            SoundtrackSongsResult = new ThemeMediaResult()
 0329        };
 330    }
 331
 332    /// <summary>
 333    /// Starts a library scan.
 334    /// </summary>
 335    /// <response code="204">Library scan started.</response>
 336    /// <returns>A <see cref="NoContentResult"/>.</returns>
 337    [HttpPost("Library/Refresh")]
 338    [Authorize(Policy = Policies.RequiresElevation)]
 339    [ProducesResponseType(StatusCodes.Status204NoContent)]
 340    public async Task<ActionResult> RefreshLibrary()
 341    {
 342        try
 343        {
 0344            await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(fa
 0345        }
 0346        catch (Exception ex)
 347        {
 0348            _logger.LogError(ex, "Error refreshing library");
 0349        }
 350
 0351        return NoContent();
 0352    }
 353
 354    /// <summary>
 355    /// Deletes an item from the library and filesystem.
 356    /// </summary>
 357    /// <param name="itemId">The item id.</param>
 358    /// <response code="204">Item deleted.</response>
 359    /// <response code="401">Unauthorized access.</response>
 360    /// <response code="404">Item not found.</response>
 361    /// <returns>A <see cref="NoContentResult"/>.</returns>
 362    [HttpDelete("Items/{itemId}")]
 363    [Authorize]
 364    [ProducesResponseType(StatusCodes.Status204NoContent)]
 365    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 366    [ProducesResponseType(StatusCodes.Status404NotFound)]
 367    public ActionResult DeleteItem(Guid itemId)
 368    {
 1369        var userId = User.GetUserId();
 1370        var isApiKey = User.GetIsApiKey();
 1371        var user = userId.IsEmpty() && isApiKey
 1372            ? null
 1373            : _userManager.GetUserById(userId);
 374
 1375        if (user is null && !isApiKey)
 376        {
 0377            return NotFound();
 378        }
 379
 1380        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1381        if (item is null)
 382        {
 1383            return NotFound();
 384        }
 385
 0386        if (user is not null && !item.CanDelete(user))
 387        {
 0388            return Unauthorized("Unauthorized access");
 389        }
 390
 0391        _libraryManager.DeleteItem(
 0392            item,
 0393            new DeleteOptions { DeleteFileLocation = true },
 0394            true);
 395
 0396        return NoContent();
 397    }
 398
 399    /// <summary>
 400    /// Deletes items from the library and filesystem.
 401    /// </summary>
 402    /// <param name="ids">The item ids.</param>
 403    /// <response code="204">Items deleted.</response>
 404    /// <response code="401">Unauthorized access.</response>
 405    /// <returns>A <see cref="NoContentResult"/>.</returns>
 406    [HttpDelete("Items")]
 407    [Authorize]
 408    [ProducesResponseType(StatusCodes.Status204NoContent)]
 409    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 410    [ProducesResponseType(StatusCodes.Status404NotFound)]
 411    public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids)
 412    {
 1413        var isApiKey = User.GetIsApiKey();
 1414        var userId = User.GetUserId();
 1415        var user = !isApiKey && !userId.IsEmpty()
 1416            ? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException()
 1417            : null;
 418
 1419        if (!isApiKey && user is null)
 420        {
 0421            return Unauthorized("Unauthorized access");
 422        }
 423
 3424        foreach (var i in ids)
 425        {
 1426            var item = _libraryManager.GetItemById<BaseItem>(i, user);
 1427            if (item is null)
 428            {
 1429                return NotFound();
 430            }
 431
 0432            if (user is not null && !item.CanDelete(user))
 433            {
 0434                return Unauthorized("Unauthorized access");
 435            }
 436
 0437            _libraryManager.DeleteItem(
 0438                item,
 0439                new DeleteOptions { DeleteFileLocation = true },
 0440                true);
 441        }
 442
 0443        return NoContent();
 444    }
 445
 446    /// <summary>
 447    /// Get item counts.
 448    /// </summary>
 449    /// <param name="userId">Optional. Get counts from a specific user's library.</param>
 450    /// <param name="isFavorite">Optional. Get counts of favorite items.</param>
 451    /// <response code="200">Item counts returned.</response>
 452    /// <returns>Item counts.</returns>
 453    [HttpGet("Items/Counts")]
 454    [Authorize]
 455    [ProducesResponseType(StatusCodes.Status200OK)]
 456    public ActionResult<ItemCounts> GetItemCounts(
 457        [FromQuery] Guid? userId,
 458        [FromQuery] bool? isFavorite)
 459    {
 0460        userId = RequestHelpers.GetUserId(User, userId);
 0461        var user = userId.IsNullOrEmpty()
 0462            ? null
 0463            : _userManager.GetUserById(userId.Value);
 464
 0465        var query = new InternalItemsQuery(user)
 0466        {
 0467            Recursive = true,
 0468            IsVirtualItem = false,
 0469            IsFavorite = isFavorite,
 0470            DtoOptions = new DtoOptions(false)
 0471            {
 0472                EnableImages = false
 0473            }
 0474        };
 475
 0476        return _libraryManager.GetItemCounts(query);
 477    }
 478
 479    /// <summary>
 480    /// Gets all parents of an item.
 481    /// </summary>
 482    /// <param name="itemId">The item id.</param>
 483    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 484    /// <response code="200">Item parents returned.</response>
 485    /// <response code="404">Item not found.</response>
 486    /// <returns>Item parents.</returns>
 487    [HttpGet("Items/{itemId}/Ancestors")]
 488    [Authorize]
 489    [ProducesResponseType(StatusCodes.Status200OK)]
 490    [ProducesResponseType(StatusCodes.Status404NotFound)]
 491    public ActionResult<IEnumerable<BaseItemDto>> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? user
 492    {
 1493        userId = RequestHelpers.GetUserId(User, userId);
 1494        var user = userId.IsNullOrEmpty()
 1495            ? null
 1496            : _userManager.GetUserById(userId.Value);
 1497        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1498        if (item is null)
 499        {
 1500            return NotFound();
 501        }
 502
 0503        var baseItemDtos = new List<BaseItemDto>();
 504
 0505        var dtoOptions = new DtoOptions();
 0506        BaseItem? parent = item.GetParent();
 507
 0508        while (parent is not null)
 509        {
 0510            if (user is not null)
 511            {
 0512                parent = TranslateParentItem(parent, user);
 0513                if (parent is null)
 514                {
 515                    break;
 516                }
 517            }
 518
 0519            baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
 520
 0521            parent = parent.GetParent();
 522        }
 523
 0524        return baseItemDtos;
 525    }
 526
 527    /// <summary>
 528    /// Gets a list of physical paths from virtual folders.
 529    /// </summary>
 530    /// <response code="200">Physical paths returned.</response>
 531    /// <returns>List of physical paths.</returns>
 532    [HttpGet("Library/PhysicalPaths")]
 533    [Authorize(Policy = Policies.RequiresElevation)]
 534    [ProducesResponseType(StatusCodes.Status200OK)]
 535    public ActionResult<IEnumerable<string>> GetPhysicalPaths()
 536    {
 0537        return Ok(_libraryManager.RootFolder.Children
 0538            .SelectMany(c => c.PhysicalLocations));
 539    }
 540
 541    /// <summary>
 542    /// Gets all user media folders.
 543    /// </summary>
 544    /// <param name="isHidden">Optional. Filter by folders that are marked hidden, or not.</param>
 545    /// <response code="200">Media folders returned.</response>
 546    /// <returns>List of user media folders.</returns>
 547    [HttpGet("Library/MediaFolders")]
 548    [Authorize(Policy = Policies.RequiresElevation)]
 549    [ProducesResponseType(StatusCodes.Status200OK)]
 550    public ActionResult<QueryResult<BaseItemDto>> GetMediaFolders([FromQuery] bool? isHidden)
 551    {
 0552        var items = _libraryManager.GetUserRootFolder().Children
 0553            .Concat(_libraryManager.RootFolder.VirtualChildren)
 0554            .Where(i => _libraryManager.GetLibraryOptions(i).Enabled)
 0555            .OrderBy(i => i.SortName)
 0556            .ToList();
 557
 0558        if (isHidden.HasValue)
 559        {
 0560            var val = isHidden.Value;
 561
 0562            items = items.Where(i => i.IsHidden == val).ToList();
 563        }
 564
 0565        var dtoOptions = new DtoOptions();
 0566        var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions);
 0567        return new QueryResult<BaseItemDto>(resultArray);
 568    }
 569
 570    /// <summary>
 571    /// Reports that new episodes of a series have been added by an external source.
 572    /// </summary>
 573    /// <param name="tvdbId">The tvdbId.</param>
 574    /// <response code="204">Report success.</response>
 575    /// <returns>A <see cref="NoContentResult"/>.</returns>
 576    [HttpPost("Library/Series/Added", Name = "PostAddedSeries")]
 577    [HttpPost("Library/Series/Updated")]
 578    [Authorize]
 579    [ProducesResponseType(StatusCodes.Status204NoContent)]
 580    public ActionResult PostUpdatedSeries([FromQuery] string? tvdbId)
 581    {
 0582        var series = _libraryManager.GetItemList(new InternalItemsQuery
 0583        {
 0584            IncludeItemTypes = new[] { BaseItemKind.Series },
 0585            DtoOptions = new DtoOptions(false)
 0586            {
 0587                EnableImages = false
 0588            }
 0589        }).Where(i => string.Equals(tvdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Tvdb), StringCo
 590
 0591        foreach (var item in series)
 592        {
 0593            _libraryMonitor.ReportFileSystemChanged(item.Path);
 594        }
 595
 0596        return NoContent();
 597    }
 598
 599    /// <summary>
 600    /// Reports that new movies have been added by an external source.
 601    /// </summary>
 602    /// <param name="tmdbId">The tmdbId.</param>
 603    /// <param name="imdbId">The imdbId.</param>
 604    /// <response code="204">Report success.</response>
 605    /// <returns>A <see cref="NoContentResult"/>.</returns>
 606    [HttpPost("Library/Movies/Added", Name = "PostAddedMovies")]
 607    [HttpPost("Library/Movies/Updated")]
 608    [Authorize]
 609    [ProducesResponseType(StatusCodes.Status204NoContent)]
 610    public ActionResult PostUpdatedMovies([FromQuery] string? tmdbId, [FromQuery] string? imdbId)
 611    {
 0612        var movies = _libraryManager.GetItemList(new InternalItemsQuery
 0613        {
 0614            IncludeItemTypes = new[] { BaseItemKind.Movie },
 0615            DtoOptions = new DtoOptions(false)
 0616            {
 0617                EnableImages = false
 0618            }
 0619        });
 620
 0621        if (!string.IsNullOrWhiteSpace(imdbId))
 622        {
 0623            movies = movies.Where(i => string.Equals(imdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvide
 624        }
 0625        else if (!string.IsNullOrWhiteSpace(tmdbId))
 626        {
 0627            movies = movies.Where(i => string.Equals(tmdbId, i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvide
 628        }
 629        else
 630        {
 0631            movies = new List<BaseItem>();
 632        }
 633
 0634        foreach (var item in movies)
 635        {
 0636            _libraryMonitor.ReportFileSystemChanged(item.Path);
 637        }
 638
 0639        return NoContent();
 640    }
 641
 642    /// <summary>
 643    /// Reports that new movies have been added by an external source.
 644    /// </summary>
 645    /// <param name="dto">The update paths.</param>
 646    /// <response code="204">Report success.</response>
 647    /// <returns>A <see cref="NoContentResult"/>.</returns>
 648    [HttpPost("Library/Media/Updated")]
 649    [Authorize]
 650    [ProducesResponseType(StatusCodes.Status204NoContent)]
 651    public ActionResult PostUpdatedMedia([FromBody, Required] MediaUpdateInfoDto dto)
 652    {
 0653        foreach (var item in dto.Updates)
 654        {
 0655            _libraryMonitor.ReportFileSystemChanged(item.Path ?? throw new ArgumentException("Item path can't be null.")
 656        }
 657
 0658        return NoContent();
 659    }
 660
 661    /// <summary>
 662    /// Downloads item media.
 663    /// </summary>
 664    /// <param name="itemId">The item id.</param>
 665    /// <response code="200">Media downloaded.</response>
 666    /// <response code="404">Item not found.</response>
 667    /// <returns>A <see cref="FileResult"/> containing the media stream.</returns>
 668    /// <exception cref="ArgumentException">User can't download or item can't be downloaded.</exception>
 669    [HttpGet("Items/{itemId}/Download")]
 670    [Authorize(Policy = Policies.Download)]
 671    [ProducesResponseType(StatusCodes.Status200OK)]
 672    [ProducesResponseType(StatusCodes.Status404NotFound)]
 673    [ProducesFile("video/*", "audio/*")]
 674    public async Task<ActionResult> GetDownload([FromRoute, Required] Guid itemId)
 675    {
 1676        var userId = User.GetUserId();
 1677        var user = userId.IsEmpty()
 1678            ? null
 1679            : _userManager.GetUserById(userId);
 1680        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1681        if (item is null)
 682        {
 1683            return NotFound();
 684        }
 685
 0686        if (user is not null)
 687        {
 0688            if (!item.CanDownload(user))
 689            {
 0690                throw new ArgumentException("Item does not support downloading");
 691            }
 692        }
 693        else
 694        {
 0695            if (!item.CanDownload())
 696            {
 0697                throw new ArgumentException("Item does not support downloading");
 698            }
 699        }
 700
 0701        if (user is not null)
 702        {
 0703            await LogDownloadAsync(item, user).ConfigureAwait(false);
 704        }
 705
 706        // Quotes are valid in linux. They'll possibly cause issues here.
 0707        var filename = Path.GetFileName(item.Path)?.Replace("\"", string.Empty, StringComparison.Ordinal);
 708
 0709        var filePath = item.Path;
 0710        if (item.IsFileProtocol)
 711        {
 712            // PhysicalFile does not work well with symlinks at the moment.
 0713            var resolved = FileSystemHelper.ResolveLinkTarget(filePath, returnFinalTarget: true);
 0714            if (resolved is not null && resolved.Exists)
 715            {
 0716                filePath = resolved.FullName;
 717            }
 718        }
 719
 0720        return PhysicalFile(filePath, MimeTypes.GetMimeType(filePath), filename, true);
 1721    }
 722
 723    /// <summary>
 724    /// Gets the collections that include the specified item.
 725    /// </summary>
 726    /// <param name="itemId">The item id.</param>
 727    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 728    /// <param name="startIndex">Optional. The index of the first record in the output.</param>
 729    /// <param name="limit">Optional. The maximum number of records to return.</param>
 730    /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
 731    /// <response code="200">Collections returned.</response>
 732    /// <response code="401">User context missing.</response>
 733    /// <response code="404">Item not found.</response>
 734    /// <returns>The collections that contain the requested item.</returns>
 735    [HttpGet("Items/{itemId}/Collections")]
 736    [Authorize]
 737    [ProducesResponseType(StatusCodes.Status200OK)]
 738    [ProducesResponseType(StatusCodes.Status404NotFound)]
 739    public ActionResult<QueryResult<BaseItemDto>> GetItemCollections(
 740        [FromRoute, Required] Guid itemId,
 741        [FromQuery] Guid? userId,
 742        [FromQuery] int? startIndex,
 743        [FromQuery] int? limit,
 744        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields)
 745    {
 1746        userId = RequestHelpers.GetUserId(User, userId);
 1747        var user = userId.IsNullOrEmpty()
 1748            ? null
 1749            : _userManager.GetUserById(userId.Value);
 750
 1751        if (user is null)
 752        {
 0753            return Unauthorized();
 754        }
 755
 1756        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 1757        if (item is null)
 758        {
 1759            return NotFound();
 760        }
 761
 0762        var dtoOptions = new DtoOptions { Fields = fields };
 763
 0764        var visibleCollections = _collectionManager
 0765            .GetCollectionsContainingItem(user, item.Id)
 0766            .OrderBy(i => i.SortName, StringComparer.OrdinalIgnoreCase)
 0767            .ThenBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0768            .ToList();
 769
 0770        IEnumerable<BaseItem> pagedCollections = visibleCollections;
 0771        if (startIndex.HasValue)
 772        {
 0773            pagedCollections = pagedCollections.Skip(startIndex.Value);
 774        }
 775
 0776        if (limit.HasValue)
 777        {
 0778            pagedCollections = pagedCollections.Take(limit.Value);
 779        }
 780
 0781        var dtos = _dtoService.GetBaseItemDtos(pagedCollections.ToList(), dtoOptions, user);
 782
 0783        return new QueryResult<BaseItemDto>(
 0784            startIndex,
 0785            visibleCollections.Count,
 0786            dtos);
 787    }
 788
 789    /// <summary>
 790    /// Gets similar items.
 791    /// </summary>
 792    /// <param name="itemId">The item id.</param>
 793    /// <param name="excludeArtistIds">Exclude artist ids.</param>
 794    /// <param name="userId">Optional. Filter by user id, and attach user data.</param>
 795    /// <param name="limit">Optional. The maximum number of records to return.</param>
 796    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 797    /// <param name="cancellationToken">The cancellation token.</param>
 798    /// <response code="200">Similar items returned.</response>
 799    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> containing the similar items.</returns>
 800    [HttpGet("Artists/{itemId}/Similar", Name = "GetSimilarArtists")]
 801    [HttpGet("Items/{itemId}/Similar")]
 802    [HttpGet("Albums/{itemId}/Similar", Name = "GetSimilarAlbums")]
 803    [HttpGet("Shows/{itemId}/Similar", Name = "GetSimilarShows")]
 804    [HttpGet("Movies/{itemId}/Similar", Name = "GetSimilarMovies")]
 805    [HttpGet("Trailers/{itemId}/Similar", Name = "GetSimilarTrailers")]
 806    [Authorize]
 807    [ProducesResponseType(StatusCodes.Status200OK)]
 808    public async Task<ActionResult<QueryResult<BaseItemDto>>> GetSimilarItems(
 809        [FromRoute, Required] Guid itemId,
 810        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 811        [FromQuery] Guid? userId,
 812        [FromQuery] int? limit,
 813        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 814        CancellationToken cancellationToken)
 815    {
 6816        userId = RequestHelpers.GetUserId(User, userId);
 6817        var user = userId.IsNullOrEmpty()
 6818            ? null
 6819            : _userManager.GetUserById(userId.Value);
 6820        var item = itemId.IsEmpty()
 6821            ? (user is null
 6822                ? _libraryManager.RootFolder
 6823                : _libraryManager.GetUserRootFolder())
 6824            : _libraryManager.GetItemById<BaseItem>(itemId, user);
 6825        if (item is null)
 826        {
 6827            return NotFound();
 828        }
 829
 0830        if (item is Episode || (item is IItemByName && item is not MusicArtist))
 831        {
 0832            return new QueryResult<BaseItemDto>();
 833        }
 834
 0835        var dtoOptions = new DtoOptions { Fields = fields };
 836
 837        // Get library options for provider configuration
 0838        var libraryOptions = _libraryManager.GetLibraryOptions(item);
 839
 0840        var itemsResult = await _similarItemsManager.GetSimilarItemsAsync(
 0841            item,
 0842            excludeArtistIds,
 0843            user,
 0844            dtoOptions,
 0845            limit,
 0846            libraryOptions,
 0847            cancellationToken).ConfigureAwait(false);
 848
 0849        var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
 850
 0851        return new QueryResult<BaseItemDto>(
 0852            0,
 0853            itemsResult.Count,
 0854            returnList);
 6855    }
 856
 857    /// <summary>
 858    /// Gets the library options info.
 859    /// </summary>
 860    /// <param name="libraryContentType">Library content type.</param>
 861    /// <param name="isNewLibrary">Whether this is a new library.</param>
 862    /// <response code="200">Library options info returned.</response>
 863    /// <returns>Library options info.</returns>
 864    [HttpGet("Libraries/AvailableOptions")]
 865    [Authorize(Policy = Policies.FirstTimeSetupOrDefault)]
 866    [ProducesResponseType(StatusCodes.Status200OK)]
 867    public ActionResult<LibraryOptionsResultDto> GetLibraryOptionsInfo(
 868        [FromQuery] CollectionType? libraryContentType,
 869        [FromQuery] bool isNewLibrary = false)
 870    {
 0871        var result = new LibraryOptionsResultDto();
 872
 0873        var types = GetRepresentativeItemTypes(libraryContentType);
 0874        var typesList = types.ToList();
 875
 0876        var plugins = _providerManager.GetAllMetadataPlugins()
 0877            .Where(i => types.Contains(i.ItemType, StringComparison.OrdinalIgnoreCase))
 0878            .OrderBy(i => typesList.IndexOf(i.ItemType))
 0879            .ToList();
 880
 0881        result.MetadataSavers = plugins
 0882            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataSaver))
 0883            .Select(i => new LibraryOptionInfoDto
 0884            {
 0885                Name = i.Name,
 0886                DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary)
 0887            })
 0888            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0889            .ToArray();
 890
 0891        result.MetadataReaders = plugins
 0892            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LocalMetadataProvider))
 0893            .Select(i => new LibraryOptionInfoDto
 0894            {
 0895                Name = i.Name,
 0896                DefaultEnabled = true
 0897            })
 0898            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0899            .ToArray();
 900
 0901        result.SubtitleFetchers = plugins
 0902            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.SubtitleFetcher))
 0903            .Select(i => new LibraryOptionInfoDto
 0904            {
 0905                Name = i.Name,
 0906                DefaultEnabled = true
 0907            })
 0908            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0909            .ToArray();
 910
 0911        result.LyricFetchers = plugins
 0912            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LyricFetcher))
 0913            .Select(i => new LibraryOptionInfoDto
 0914            {
 0915                Name = i.Name,
 0916                DefaultEnabled = true
 0917            })
 0918            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0919            .ToArray();
 920
 0921        result.MediaSegmentProviders = plugins
 0922            .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MediaSegmentProvider))
 0923            .Select(i => new LibraryOptionInfoDto
 0924            {
 0925                Name = i.Name,
 0926                DefaultEnabled = true
 0927            })
 0928            .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0929            .ToArray();
 930
 0931        var typeOptions = new List<LibraryTypeOptionsDto>();
 932
 0933        foreach (var type in types)
 934        {
 0935            TypeOptions.DefaultImageOptions.TryGetValue(type, out var defaultImageOptions);
 936
 0937            typeOptions.Add(new LibraryTypeOptionsDto
 0938            {
 0939                Type = type,
 0940
 0941                MetadataFetchers = plugins
 0942                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0943                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher))
 0944                    .Select(i => new LibraryOptionInfoDto
 0945                    {
 0946                        Name = i.Name,
 0947                        DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary)
 0948                    })
 0949                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0950                    .ToArray(),
 0951
 0952                ImageFetchers = plugins
 0953                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0954                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher))
 0955                    .Select(i => new LibraryOptionInfoDto
 0956                    {
 0957                        Name = i.Name,
 0958                        DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary)
 0959                    })
 0960                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0961                    .ToArray(),
 0962
 0963                SimilarItemProviders = plugins
 0964                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0965                    .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LocalSimilarityProvider || p.Type
 0966                    .Select(i => new LibraryOptionInfoDto
 0967                    {
 0968                        Name = i.Name,
 0969                        DefaultEnabled = i.Type == MetadataPluginType.LocalSimilarityProvider
 0970                    })
 0971                    .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
 0972                    .ToArray(),
 0973
 0974                SupportedImageTypes = plugins
 0975                    .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase))
 0976                    .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>())
 0977                    .Distinct()
 0978                    .ToArray(),
 0979
 0980                DefaultImageOptions = defaultImageOptions ?? Array.Empty<ImageOption>()
 0981            });
 982        }
 983
 0984        result.TypeOptions = typeOptions.ToArray();
 985
 0986        return result;
 987    }
 988
 989    private BaseItem? TranslateParentItem(BaseItem item, User user)
 990    {
 0991        return item.GetParent() is AggregateFolder
 0992            ? _libraryManager.GetUserRootFolder().GetChildren(user, true)
 0993                .FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path))
 0994            : item;
 995    }
 996
 997    private async Task LogDownloadAsync(BaseItem item, User user)
 998    {
 999        try
 1000        {
 01001            await _activityManager.CreateAsync(new ActivityLog(
 01002                string.Format(CultureInfo.InvariantCulture, _localization.GetServerLocalizedString("UserDownloadingItemW
 01003                "UserDownloadingContent",
 01004                User.GetUserId())
 01005            {
 01006                ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetServerLocalizedString("AppD
 01007                ItemId = item.Id.ToString("N", CultureInfo.InvariantCulture)
 01008            }).ConfigureAwait(false);
 01009        }
 01010        catch
 1011        {
 1012            // Logged at lower levels
 01013        }
 01014    }
 1015
 1016    private static string[] GetRepresentativeItemTypes(CollectionType? contentType)
 1017    {
 01018        return contentType switch
 01019        {
 01020            CollectionType.boxsets => new[] { "BoxSet" },
 01021            CollectionType.playlists => new[] { "Playlist" },
 01022            CollectionType.movies => new[] { "Movie" },
 01023            CollectionType.tvshows => new[] { "Series", "Season", "Episode" },
 01024            CollectionType.books => new[] { "Book", "AudioBook" },
 01025            CollectionType.music => new[] { "MusicArtist", "MusicAlbum", "Audio", "MusicVideo" },
 01026            CollectionType.homevideos => new[] { "Video", "Photo" },
 01027            CollectionType.photos => new[] { "Video", "Photo" },
 01028            CollectionType.musicvideos => new[] { "MusicVideo" },
 01029            _ => new[] { "Series", "Season", "Episode", "Movie" }
 01030        };
 1031    }
 1032
 1033    private bool IsSaverEnabledByDefault(string name, string[] itemTypes, bool isNewLibrary)
 1034    {
 01035        if (isNewLibrary)
 1036        {
 01037            return false;
 1038        }
 1039
 01040        var metadataOptions = _serverConfigurationManager.Configuration.MetadataOptions
 01041            .Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
 01042            .ToArray();
 1043
 01044        return metadataOptions.Length == 0 || metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringCo
 1045    }
 1046
 1047    private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
 1048    {
 01049        if (isNewLibrary)
 1050        {
 01051            if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
 1052            {
 01053                return !(string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
 01054                         || string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
 01055                         || string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase));
 1056            }
 1057
 01058            return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
 01059                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
 01060                   || string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase);
 1061        }
 1062
 01063        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
 01064        return metadataOptions is null || !metadataOptions.DisabledMetadataFetchers.Contains(name, StringComparison.Ordi
 1065    }
 1066
 1067    private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary)
 1068    {
 01069        if (isNewLibrary)
 1070        {
 01071            if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase))
 1072            {
 01073                return !string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase)
 01074                       && !string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)
 01075                       && !string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)
 01076                       && !string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase);
 1077            }
 1078
 01079            return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
 01080                   || string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase)
 01081                   || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
 01082                   || string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase);
 1083        }
 1084
 01085        var metadataOptions = _serverConfigurationManager.GetMetadataOptionsForType(type);
 01086        return metadataOptions is null || !metadataOptions.DisabledImageFetchers.Contains(name, StringComparison.Ordinal
 1087    }
 1088}

Methods/Properties

.ctor(MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.ISimilarItemsManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Collections.ICollectionManager,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()
GetItemCollections(System.Guid,System.Nullable`1<System.Guid>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,MediaBrowser.Model.Querying.ItemFields[])
GetSimilarItems()
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)