< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Validators.ArtistsValidator
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
Line coverage
55%
Covered lines: 27
Uncovered lines: 22
Coverable lines: 49
Total lines: 122
Line coverage: 55.1%
Branch coverage
16%
Covered branches: 2
Total branches: 12
Branch coverage: 16.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 1/23/2026 - 12:11:06 AM Line coverage: 100% (4/4) Total lines: 1104/15/2026 - 12:14:34 AM Line coverage: 100% (4/4) Total lines: 1194/19/2026 - 12:14:27 AM Line coverage: 44.6% (21/47) Branch coverage: 37.5% (3/8) Total lines: 1195/4/2026 - 12:15:16 AM Line coverage: 55.1% (27/49) Branch coverage: 16.6% (2/12) Total lines: 122 4/19/2026 - 12:14:27 AM Line coverage: 44.6% (21/47) Branch coverage: 37.5% (3/8) Total lines: 1195/4/2026 - 12:15:16 AM Line coverage: 55.1% (27/49) Branch coverage: 16.6% (2/12) Total lines: 122

Coverage delta

Coverage delta 56 -56

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Run()16.66%291251.11%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/Validators/ArtistsValidator.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.Entities.Audio;
 9using MediaBrowser.Controller.Library;
 10using MediaBrowser.Controller.Persistence;
 11using Microsoft.Extensions.Logging;
 12
 13namespace Emby.Server.Implementations.Library.Validators;
 14
 15/// <summary>
 16/// Class ArtistsValidator.
 17/// </summary>
 18public class ArtistsValidator
 19{
 20    /// <summary>
 21    /// The library manager.
 22    /// </summary>
 23    private readonly ILibraryManager _libraryManager;
 24
 25    /// <summary>
 26    /// The logger.
 27    /// </summary>
 28    private readonly ILogger<ArtistsValidator> _logger;
 29    private readonly IItemRepository _itemRepo;
 30
 31    /// <summary>
 32    /// Initializes a new instance of the <see cref="ArtistsValidator" /> 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 ArtistsValidator(ILibraryManager libraryManager, ILogger<ArtistsValidator> 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.GetAllArtistNames();
 1653        var existingArtistIds = _libraryManager.GetItemIds(new InternalItemsQuery
 1654        {
 1655            IncludeItemTypes = [BaseItemKind.MusicArtist]
 1656        }).ToHashSet();
 57
 1658        var existingArtists = _libraryManager.GetArtists(names);
 59
 1660        var numComplete = 0;
 1661        var count = names.Count;
 1662        var refreshed = 0;
 63
 3264        foreach (var name in names)
 65        {
 66            try
 67            {
 068                MusicArtist? item = null;
 069                if (existingArtists.TryGetValue(name, out var artists) && artists.Length > 0)
 70                {
 071                    item = artists.OrderBy(i => i.IsAccessedByName ? 1 : 0).First();
 72                }
 73
 74                // Fall back to GetArtist if not found (creates new item if needed)
 075                item ??= _libraryManager.GetArtist(name);
 076                var isNew = !existingArtistIds.Contains(item.Id);
 077                var neverRefreshed = item.DateLastRefreshed == default;
 78
 079                if (isNew || neverRefreshed)
 80                {
 081                    await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
 082                    refreshed++;
 83                }
 084            }
 085            catch (OperationCanceledException)
 86            {
 087                throw;
 88            }
 089            catch (Exception ex)
 90            {
 091                _logger.LogError(ex, "Error refreshing {ArtistName}", name);
 092            }
 93
 094            numComplete++;
 095            double percent = numComplete;
 096            percent /= count;
 097            percent *= 100;
 98
 099            progress.Report(percent);
 0100        }
 101
 16102        _logger.LogInformation("Refreshed metadata for {RefreshedCount} new artists out of {TotalCount} total", refreshe
 103
 16104        var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
 16105        {
 16106            IncludeItemTypes = [BaseItemKind.MusicArtist],
 16107            IsDeadArtist = true,
 16108            IsLocked = false
 16109        }).Cast<MusicArtist>()
 16110        .Where(item => item.IsAccessedByName)
 16111        .ToList();
 112
 32113        foreach (var item in deadEntities)
 114        {
 0115            _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString
 116        }
 117
 16118        _libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
 119
 16120        progress.Report(100);
 16121    }
 122}