< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Lyric.LyricScheduledTask
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
Line coverage
16%
Covered lines: 14
Uncovered lines: 73
Coverable lines: 87
Total lines: 171
Line coverage: 16%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
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: 66.6% (14/21) Total lines: 1714/19/2026 - 12:14:27 AM Line coverage: 16% (14/87) Branch coverage: 0% (0/10) Total lines: 171 4/19/2026 - 12:14:27 AM Line coverage: 16% (14/87) Branch coverage: 0% (0/10) Total lines: 171

Coverage delta

Coverage delta 51 -51

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
.ctor(...)100%11100%
get_Name()100%11100%
get_Key()100%210%
get_Description()100%210%
get_Category()100%210%
ExecuteAsync()0%110100%
GetDefaultTriggers()100%11100%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Threading;
 5using System.Threading.Tasks;
 6using Jellyfin.Data.Enums;
 7using MediaBrowser.Controller.Dto;
 8using MediaBrowser.Controller.Entities;
 9using MediaBrowser.Controller.Entities.Audio;
 10using MediaBrowser.Controller.Library;
 11using MediaBrowser.Controller.Lyrics;
 12using MediaBrowser.Model.Entities;
 13using MediaBrowser.Model.Globalization;
 14using MediaBrowser.Model.Lyrics;
 15using MediaBrowser.Model.Tasks;
 16using Microsoft.Extensions.Logging;
 17
 18namespace MediaBrowser.Providers.Lyric;
 19
 20/// <summary>
 21/// Task to download lyrics.
 22/// </summary>
 23public class LyricScheduledTask : IScheduledTask
 24{
 25    private const int QueryPageLimit = 100;
 26
 027    private static readonly BaseItemKind[] _itemKinds = [BaseItemKind.Audio];
 028    private static readonly MediaType[] _mediaTypes = [MediaType.Audio];
 029    private static readonly SourceType[] _sourceTypes = [SourceType.Library];
 030    private static readonly DtoOptions _dtoOptions = new(false);
 31
 32    private readonly ILibraryManager _libraryManager;
 33    private readonly ILyricManager _lyricManager;
 34    private readonly ILogger<LyricScheduledTask> _logger;
 35    private readonly ILocalizationManager _localizationManager;
 36
 37    /// <summary>
 38    /// Initializes a new instance of the <see cref="LyricScheduledTask"/> class.
 39    /// </summary>
 40    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 41    /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
 42    /// <param name="logger">Instance of the <see cref="ILogger{DownloaderScheduledTask}"/> interface.</param>
 43    /// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 44    public LyricScheduledTask(
 45        ILibraryManager libraryManager,
 46        ILyricManager lyricManager,
 47        ILogger<LyricScheduledTask> logger,
 48        ILocalizationManager localizationManager)
 49    {
 2150        _libraryManager = libraryManager;
 2151        _lyricManager = lyricManager;
 2152        _logger = logger;
 2153        _localizationManager = localizationManager;
 2154    }
 55
 56    /// <inheritdoc />
 2157    public string Name => _localizationManager.GetLocalizedString("TaskDownloadMissingLyrics");
 58
 59    /// <inheritdoc />
 060    public string Key => "DownloadLyrics";
 61
 62    /// <inheritdoc />
 063    public string Description => _localizationManager.GetLocalizedString("TaskDownloadMissingLyricsDescription");
 64
 65    /// <inheritdoc />
 066    public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");
 67
 68    /// <inheritdoc />
 69    public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
 70    {
 071        var totalCount = _libraryManager.GetCount(new InternalItemsQuery
 072        {
 073            Recursive = true,
 074            IsVirtualItem = false,
 075            IncludeItemTypes = _itemKinds,
 076            DtoOptions = _dtoOptions,
 077            MediaTypes = _mediaTypes,
 078            SourceTypes = _sourceTypes
 079        });
 80
 081        var completed = 0;
 82
 083        foreach (var library in _libraryManager.RootFolder.Children.ToList())
 84        {
 085            var libraryOptions = _libraryManager.GetLibraryOptions(library);
 086            var itemQuery = new InternalItemsQuery
 087            {
 088                Recursive = true,
 089                IsVirtualItem = false,
 090                IncludeItemTypes = _itemKinds,
 091                DtoOptions = _dtoOptions,
 092                MediaTypes = _mediaTypes,
 093                SourceTypes = _sourceTypes,
 094                Limit = QueryPageLimit,
 095                Parent = library
 096            };
 97
 98            int previousCount;
 099            var startIndex = 0;
 100            do
 101            {
 0102                itemQuery.StartIndex = startIndex;
 0103                var audioItems = _libraryManager.GetItemList(itemQuery);
 104
 0105                foreach (var audioItem in audioItems.OfType<Audio>())
 106                {
 0107                    cancellationToken.ThrowIfCancellationRequested();
 108
 109                    try
 110                    {
 0111                        if (audioItem.GetMediaStreams().All(s => s.Type != MediaStreamType.Lyric))
 112                        {
 0113                            _logger.LogDebug("Searching for lyrics for {Path}", audioItem.Path);
 0114                            var lyricResults = await _lyricManager.SearchLyricsAsync(
 0115                                    new LyricSearchRequest
 0116                                    {
 0117                                        MediaPath = audioItem.Path,
 0118                                        SongName = audioItem.Name,
 0119                                        AlbumName = audioItem.Album,
 0120                                        AlbumArtistsNames = audioItem.AlbumArtists,
 0121                                        ArtistNames = audioItem.Artists,
 0122                                        Duration = audioItem.RunTimeTicks,
 0123                                        IsAutomated = true,
 0124                                        DisabledLyricFetchers = libraryOptions.DisabledLyricFetchers,
 0125                                        LyricFetcherOrder = libraryOptions.LyricFetcherOrder
 0126                                    },
 0127                                    cancellationToken)
 0128                                .ConfigureAwait(false);
 129
 0130                            if (lyricResults.Count != 0)
 131                            {
 0132                                _logger.LogDebug("Saving lyrics for {Path}", audioItem.Path);
 0133                                await _lyricManager.DownloadLyricsAsync(
 0134                                        audioItem,
 0135                                        libraryOptions,
 0136                                        lyricResults[0].Id,
 0137                                        cancellationToken)
 0138                                    .ConfigureAwait(false);
 139                            }
 140                        }
 0141                    }
 0142                    catch (Exception ex)
 143                    {
 0144                        _logger.LogError(ex, "Error downloading lyrics for {Path}", audioItem.Path);
 0145                    }
 146
 0147                    completed++;
 0148                    progress.Report(100d * completed / totalCount);
 0149                }
 150
 0151                startIndex += QueryPageLimit;
 0152                previousCount = audioItems.Count;
 0153            } while (previousCount > 0);
 0154        }
 155
 0156        progress.Report(100);
 0157    }
 158
 159    /// <inheritdoc />
 160    public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
 161    {
 21162        return
 21163        [
 21164            new TaskTriggerInfo
 21165            {
 21166                Type = TaskTriggerInfoType.IntervalTrigger,
 21167                IntervalTicks = TimeSpan.FromHours(24).Ticks
 21168            }
 21169        ];
 170    }
 171}