< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Validators.StudiosValidator
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
Line coverage
60%
Covered lines: 30
Uncovered lines: 20
Coverable lines: 50
Total lines: 124
Line coverage: 60%
Branch coverage
20%
Covered branches: 2
Total branches: 10
Branch coverage: 20%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 100% (4/4) Total lines: 1044/19/2026 - 12:14:27 AM Line coverage: 43.5% (17/39) Branch coverage: 50% (2/4) Total lines: 1045/4/2026 - 12:15:16 AM Line coverage: 60% (30/50) Branch coverage: 20% (2/10) Total lines: 124 4/19/2026 - 12:14:27 AM Line coverage: 43.5% (17/39) Branch coverage: 50% (2/4) Total lines: 1045/4/2026 - 12:15:16 AM Line coverage: 60% (30/50) Branch coverage: 20% (2/10) Total lines: 124

Coverage delta

Coverage delta 57 -57

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Run()20%181056.52%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.Linq;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Jellyfin.Data.Enums;
 7using MediaBrowser.Controller.Entities;
 8using MediaBrowser.Controller.Library;
 9using MediaBrowser.Controller.Persistence;
 10using Microsoft.Extensions.Logging;
 11
 12namespace Emby.Server.Implementations.Library.Validators;
 13
 14/// <summary>
 15/// Class StudiosValidator.
 16/// </summary>
 17public class StudiosValidator
 18{
 19    /// <summary>
 20    /// The library manager.
 21    /// </summary>
 22    private readonly ILibraryManager _libraryManager;
 23
 24    private readonly IItemRepository _itemRepo;
 25
 26    /// <summary>
 27    /// The logger.
 28    /// </summary>
 29    private readonly ILogger<StudiosValidator> _logger;
 30
 31    /// <summary>
 32    /// Initializes a new instance of the <see cref="StudiosValidator" /> class.
 33    /// </summary>
 34    /// <param name="libraryManager">The library manager.</param>
 35    /// <param name="logger">The logger.</param>
 36    /// <param name="itemRepo">The item repository.</param>
 37    public StudiosValidator(ILibraryManager libraryManager, ILogger<StudiosValidator> logger, IItemRepository itemRepo)
 38    {
 1639        _libraryManager = libraryManager;
 1640        _logger = logger;
 1641        _itemRepo = itemRepo;
 1642    }
 43
 44    /// <summary>
 45    /// Runs the specified progress.
 46    /// </summary>
 47    /// <param name="progress">The progress.</param>
 48    /// <param name="cancellationToken">The cancellation token.</param>
 49    /// <returns>Task.</returns>
 50    public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
 51    {
 1652        var names = _itemRepo.GetStudioNames();
 1653        var existingStudioIds = _libraryManager.GetItemIds(new InternalItemsQuery
 1654        {
 1655            IncludeItemTypes = [BaseItemKind.Studio]
 1656        }).ToHashSet();
 57
 1658        var existingStudios = _libraryManager.GetItemList(new InternalItemsQuery
 1659        {
 1660            IncludeItemTypes = [BaseItemKind.Studio]
 1661        }).Cast<Studio>()
 1662        .GroupBy(s => s.Name, StringComparer.OrdinalIgnoreCase)
 1663        .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
 64
 1665        var numComplete = 0;
 1666        var count = names.Count;
 1667        var refreshed = 0;
 68
 3269        foreach (var name in names)
 70        {
 71            try
 72            {
 073                Studio? item = null;
 074                if (existingStudios.TryGetValue(name, out var existingStudio))
 75                {
 076                    item = existingStudio;
 77                }
 78
 79                // Fall back to GetStudio if not found (creates new item if needed)
 080                item ??= _libraryManager.GetStudio(name);
 81
 082                if (!existingStudioIds.Contains(item.Id))
 83                {
 084                    await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
 085                    refreshed++;
 86                }
 087            }
 088            catch (OperationCanceledException)
 89            {
 90                // Don't clutter the log
 091                throw;
 92            }
 093            catch (Exception ex)
 94            {
 095                _logger.LogError(ex, "Error refreshing {StudioName}", name);
 096            }
 97
 098            numComplete++;
 099            double percent = numComplete;
 0100            percent /= count;
 0101            percent *= 100;
 102
 0103            progress.Report(percent);
 0104        }
 105
 16106        _logger.LogInformation("Refreshed metadata for {RefreshedCount} new studios out of {TotalCount} total", refreshe
 107
 16108        var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
 16109        {
 16110            IncludeItemTypes = [BaseItemKind.Studio],
 16111            IsDeadStudio = true,
 16112            IsLocked = false
 16113        });
 114
 32115        foreach (var item in deadEntities)
 116        {
 0117            _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString
 118        }
 119
 16120        _libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
 121
 16122        progress.Report(100);
 16123    }
 124}