LyricScheduledTask.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Jellyfin.Data.Enums;
  7. using MediaBrowser.Controller.Dto;
  8. using MediaBrowser.Controller.Entities;
  9. using MediaBrowser.Controller.Entities.Audio;
  10. using MediaBrowser.Controller.Library;
  11. using MediaBrowser.Controller.Lyrics;
  12. using MediaBrowser.Model.Entities;
  13. using MediaBrowser.Model.Globalization;
  14. using MediaBrowser.Model.Lyrics;
  15. using MediaBrowser.Model.Tasks;
  16. using Microsoft.Extensions.Logging;
  17. namespace MediaBrowser.Providers.Lyric;
  18. /// <summary>
  19. /// Task to download lyrics.
  20. /// </summary>
  21. public class LyricScheduledTask : IScheduledTask
  22. {
  23. private const int QueryPageLimit = 100;
  24. private static readonly BaseItemKind[] _itemKinds = [BaseItemKind.Audio];
  25. private static readonly MediaType[] _mediaTypes = [MediaType.Audio];
  26. private static readonly SourceType[] _sourceTypes = [SourceType.Library];
  27. private static readonly DtoOptions _dtoOptions = new(false);
  28. private readonly ILibraryManager _libraryManager;
  29. private readonly ILyricManager _lyricManager;
  30. private readonly ILogger<LyricScheduledTask> _logger;
  31. private readonly ILocalizationManager _localizationManager;
  32. /// <summary>
  33. /// Initializes a new instance of the <see cref="LyricScheduledTask"/> class.
  34. /// </summary>
  35. /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
  36. /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
  37. /// <param name="logger">Instance of the <see cref="ILogger{DownloaderScheduledTask}"/> interface.</param>
  38. /// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param>
  39. public LyricScheduledTask(
  40. ILibraryManager libraryManager,
  41. ILyricManager lyricManager,
  42. ILogger<LyricScheduledTask> logger,
  43. ILocalizationManager localizationManager)
  44. {
  45. _libraryManager = libraryManager;
  46. _lyricManager = lyricManager;
  47. _logger = logger;
  48. _localizationManager = localizationManager;
  49. }
  50. /// <inheritdoc />
  51. public string Name => _localizationManager.GetLocalizedString("TaskDownloadMissingLyrics");
  52. /// <inheritdoc />
  53. public string Key => "DownloadLyrics";
  54. /// <inheritdoc />
  55. public string Description => _localizationManager.GetLocalizedString("TaskDownloadMissingLyricsDescription");
  56. /// <inheritdoc />
  57. public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");
  58. /// <inheritdoc />
  59. public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
  60. {
  61. var totalCount = _libraryManager.GetCount(new InternalItemsQuery
  62. {
  63. Recursive = true,
  64. IsVirtualItem = false,
  65. IncludeItemTypes = _itemKinds,
  66. DtoOptions = _dtoOptions,
  67. MediaTypes = _mediaTypes,
  68. SourceTypes = _sourceTypes
  69. });
  70. var completed = 0;
  71. foreach (var library in _libraryManager.RootFolder.Children.ToList())
  72. {
  73. var libraryOptions = _libraryManager.GetLibraryOptions(library);
  74. var itemQuery = new InternalItemsQuery
  75. {
  76. Recursive = true,
  77. IsVirtualItem = false,
  78. IncludeItemTypes = _itemKinds,
  79. DtoOptions = _dtoOptions,
  80. MediaTypes = _mediaTypes,
  81. SourceTypes = _sourceTypes,
  82. Limit = QueryPageLimit,
  83. Parent = library
  84. };
  85. int previousCount;
  86. var startIndex = 0;
  87. do
  88. {
  89. itemQuery.StartIndex = startIndex;
  90. var audioItems = _libraryManager.GetItemList(itemQuery);
  91. foreach (var audioItem in audioItems.OfType<Audio>())
  92. {
  93. cancellationToken.ThrowIfCancellationRequested();
  94. try
  95. {
  96. if (audioItem.GetMediaStreams().All(s => s.Type != MediaStreamType.Lyric))
  97. {
  98. _logger.LogDebug("Searching for lyrics for {Path}", audioItem.Path);
  99. var lyricResults = await _lyricManager.SearchLyricsAsync(
  100. new LyricSearchRequest
  101. {
  102. MediaPath = audioItem.Path,
  103. SongName = audioItem.Name,
  104. AlbumName = audioItem.Album,
  105. ArtistNames = audioItem.GetAllArtists().ToList(),
  106. Duration = audioItem.RunTimeTicks,
  107. IsAutomated = true,
  108. DisabledLyricFetchers = libraryOptions.DisabledLyricFetchers,
  109. LyricFetcherOrder = libraryOptions.LyricFetcherOrder
  110. },
  111. cancellationToken)
  112. .ConfigureAwait(false);
  113. if (lyricResults.Count != 0)
  114. {
  115. _logger.LogDebug("Saving lyrics for {Path}", audioItem.Path);
  116. await _lyricManager.DownloadLyricsAsync(
  117. audioItem,
  118. libraryOptions,
  119. lyricResults[0].Id,
  120. cancellationToken)
  121. .ConfigureAwait(false);
  122. }
  123. }
  124. }
  125. catch (Exception ex)
  126. {
  127. _logger.LogError(ex, "Error downloading lyrics for {Path}", audioItem.Path);
  128. }
  129. completed++;
  130. progress.Report(100d * completed / totalCount);
  131. }
  132. startIndex += QueryPageLimit;
  133. previousCount = audioItems.Count;
  134. } while (previousCount > 0);
  135. }
  136. progress.Report(100);
  137. }
  138. /// <inheritdoc />
  139. public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
  140. {
  141. return
  142. [
  143. new TaskTriggerInfo
  144. {
  145. Type = TaskTriggerInfoType.IntervalTrigger,
  146. IntervalTicks = TimeSpan.FromHours(24).Ticks
  147. }
  148. ];
  149. }
  150. }