diff options
| -rw-r--r-- | Emby.Server.Implementations/Localization/Core/en-US.json | 2 | ||||
| -rw-r--r-- | MediaBrowser.Providers/Lyric/LyricScheduledTask.cs | 171 |
2 files changed, 173 insertions, 0 deletions
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index 1a69627fa..f8f962ee0 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -122,6 +122,8 @@ "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.", "TaskRefreshChannels": "Refresh Channels", "TaskRefreshChannelsDescription": "Refreshes internet channel information.", + "TaskDownloadMissingLyrics": "Download missing lyrics", + "TaskDownloadMissingLyricsDescription": "Task to download missing lyrics", "TaskDownloadMissingSubtitles": "Download missing subtitles", "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.", "TaskOptimizeDatabase": "Optimize database", diff --git a/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs new file mode 100644 index 000000000..184e8495b --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Lyrics; +using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Lyric; + +/// <summary> +/// Task to download lyrics. +/// </summary> +public class LyricDownloadTask : IScheduledTask +{ + private const int QueryPageLimit = 100; + + private static readonly BaseItemKind[] _itemKinds = [BaseItemKind.Audio]; + private static readonly MediaType[] _mediaTypes = [MediaType.Audio]; + private static readonly SourceType[] _sourceTypes = [SourceType.Library]; + private static readonly DtoOptions _dtoOptions = new(false); + + private readonly ILibraryManager _libraryManager; + private readonly ILyricManager _lyricManager; + private readonly ILogger<LyricDownloadTask> _logger; + private readonly ILocalizationManager _localizationManager; + + /// <summary> + /// Initializes a new instance of the <see cref="LyricDownloadTask"/> class. + /// </summary> + /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> + /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param> + /// <param name="logger">Instance of the <see cref="ILogger{DownloaderScheduledTask}"/> interface.</param> + /// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param> + public LyricDownloadTask( + ILibraryManager libraryManager, + ILyricManager lyricManager, + ILogger<LyricDownloadTask> logger, + ILocalizationManager localizationManager) + { + _libraryManager = libraryManager; + _lyricManager = lyricManager; + _logger = logger; + _localizationManager = localizationManager; + } + + /// <inheritdoc /> + public string Name => _localizationManager.GetLocalizedString("TaskDownloadMissingLyrics"); + + /// <inheritdoc /> + public string Key => "DownloadLrcLibLyrics"; + + /// <inheritdoc /> + public string Description => _localizationManager.GetLocalizedString("TaskDownloadMissingLyricsDescription"); + + /// <inheritdoc /> + public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory"); + + /// <inheritdoc /> + public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken) + { + var totalCount = _libraryManager.GetCount(new InternalItemsQuery + { + Recursive = true, + IsVirtualItem = false, + IncludeItemTypes = _itemKinds, + DtoOptions = _dtoOptions, + MediaTypes = _mediaTypes, + SourceTypes = _sourceTypes + }); + + var completed = 0; + + foreach (var library in _libraryManager.RootFolder.Children.ToList()) + { + var libraryOptions = _libraryManager.GetLibraryOptions(library); + var itemQuery = new InternalItemsQuery + { + Recursive = true, + IsVirtualItem = false, + IncludeItemTypes = _itemKinds, + DtoOptions = _dtoOptions, + MediaTypes = _mediaTypes, + SourceTypes = _sourceTypes, + Limit = QueryPageLimit, + Parent = library + }; + + int previousCount; + var startIndex = 0; + do + { + itemQuery.StartIndex = startIndex; + var audioItems = _libraryManager.GetItemList(itemQuery); + + foreach (var audioItem in audioItems.OfType<Audio>()) + { + cancellationToken.ThrowIfCancellationRequested(); + + try + { + if (audioItem.GetMediaStreams().All(s => s.Type != MediaStreamType.Lyric)) + { + _logger.LogDebug("Searching for lyrics for {Path}", audioItem.Path); + var lyricResults = await _lyricManager.SearchLyricsAsync( + new LyricSearchRequest + { + MediaPath = audioItem.Path, + SongName = audioItem.Name, + AlbumName = audioItem.Album, + ArtistNames = audioItem.GetAllArtists().ToList(), + Duration = audioItem.RunTimeTicks, + IsAutomated = true, + DisabledLyricFetchers = libraryOptions.DisabledLyricFetchers, + LyricFetcherOrder = libraryOptions.LyricFetcherOrder + }, + cancellationToken) + .ConfigureAwait(false); + + if (lyricResults.Count != 0) + { + _logger.LogDebug("Saving lyrics for {Path}", audioItem.Path); + await _lyricManager.DownloadLyricsAsync( + audioItem, + libraryOptions, + lyricResults[0].Id, + cancellationToken) + .ConfigureAwait(false); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error downloading lyrics for {Path}", audioItem.Path); + } + + completed++; + progress.Report(100d * completed / totalCount); + } + + startIndex += QueryPageLimit; + previousCount = audioItems.Count; + + } while (previousCount > 0); + } + + progress.Report(100); + } + + /// <inheritdoc /> + public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() + { + return + [ + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, + IntervalTicks = TimeSpan.FromHours(24).Ticks + } + ]; + } +} |
