LyricScheduledTask.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. AlbumArtistsNames = audioItem.AlbumArtists,
  106. ArtistNames = audioItem.Artists,
  107. Duration = audioItem.RunTimeTicks,
  108. IsAutomated = true,
  109. DisabledLyricFetchers = libraryOptions.DisabledLyricFetchers,
  110. LyricFetcherOrder = libraryOptions.LyricFetcherOrder
  111. },
  112. cancellationToken)
  113. .ConfigureAwait(false);
  114. if (lyricResults.Count != 0)
  115. {
  116. _logger.LogDebug("Saving lyrics for {Path}", audioItem.Path);
  117. await _lyricManager.DownloadLyricsAsync(
  118. audioItem,
  119. libraryOptions,
  120. lyricResults[0].Id,
  121. cancellationToken)
  122. .ConfigureAwait(false);
  123. }
  124. }
  125. }
  126. catch (Exception ex)
  127. {
  128. _logger.LogError(ex, "Error downloading lyrics for {Path}", audioItem.Path);
  129. }
  130. completed++;
  131. progress.Report(100d * completed / totalCount);
  132. }
  133. startIndex += QueryPageLimit;
  134. previousCount = audioItems.Count;
  135. } while (previousCount > 0);
  136. }
  137. progress.Report(100);
  138. }
  139. /// <inheritdoc />
  140. public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
  141. {
  142. return
  143. [
  144. new TaskTriggerInfo
  145. {
  146. Type = TaskTriggerInfoType.IntervalTrigger,
  147. IntervalTicks = TimeSpan.FromHours(24).Ticks
  148. }
  149. ];
  150. }
  151. }